diff --git a/docs/ai/quickstarts/use-function-calling.md b/docs/ai/quickstarts/use-function-calling.md index c62fe7d5f1957..04fc10b0d71c8 100644 --- a/docs/ai/quickstarts/use-function-calling.md +++ b/docs/ai/quickstarts/use-function-calling.md @@ -1,7 +1,7 @@ --- title: Quickstart - Extend OpenAI using Tools and execute a local Function with .NET description: Create a simple chat app using OpenAI and extend the model to execute a local function. -ms.date: 07/14/2024 +ms.date: 03/13/2025 ms.topic: quickstart ms.custom: devx-track-dotnet, devx-track-dotnet-ai author: fboucher @@ -56,8 +56,8 @@ Complete the following steps to create a .NET console app to connect to an AI mo ```bash dotnet add package Azure.Identity dotnet add package Azure.AI.OpenAI - dotnet add package Microsoft.Extensions.AI - dotnet add package Microsoft.Extensions.AI.OpenAI + dotnet add package Microsoft.Extensions.AI --prerelease + dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease dotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.UserSecrets ``` @@ -67,8 +67,8 @@ Complete the following steps to create a .NET console app to connect to an AI mo :::zone target="docs" pivot="openai" ```bash - dotnet add package Microsoft.Extensions.AI - dotnet add package Microsoft.Extensions.AI.OpenAI + dotnet add package Microsoft.Extensions.AI --prerelease + dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease dotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.UserSecrets ``` diff --git a/docs/azure/includes/dotnet-all.md b/docs/azure/includes/dotnet-all.md index 9990b7dfc76f1..118d8d7d685ab 100644 --- a/docs/azure/includes/dotnet-all.md +++ b/docs/azure/includes/dotnet-all.md @@ -175,7 +175,7 @@ | Resource Management - Application Insights | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.ApplicationInsights/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.ApplicationInsights-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ApplicationInsights_1.0.1/sdk/applicationinsights/Azure.ResourceManager.ApplicationInsights/) | | Resource Management - Astro | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.Astro/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.Astro-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Astro_1.0.0-beta.2/sdk/astronomer/Azure.ResourceManager.Astro/) | | Resource Management - Attestation | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Attestation/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Attestation-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Attestation_1.0.0-beta.4/sdk/attestation/Azure.ResourceManager.Attestation/) | -| Resource Management - Authorization | NuGet [1.1.3](https://www.nuget.org/packages/Azure.ResourceManager.Authorization/1.1.3) | [docs](/dotnet/api/overview/azure/ResourceManager.Authorization-readme) | GitHub [1.1.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Authorization_1.1.3/sdk/authorization/Azure.ResourceManager.Authorization/) | +| Resource Management - Authorization | NuGet [1.1.4](https://www.nuget.org/packages/Azure.ResourceManager.Authorization/1.1.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Authorization-readme) | GitHub [1.1.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Authorization_1.1.4/sdk/authorization/Azure.ResourceManager.Authorization/) | | Resource Management - Automanage | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Automanage/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Automanage-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Automanage_1.1.0/sdk/automanage/Azure.ResourceManager.Automanage/) | | Resource Management - Automation | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Automation/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Automation-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Automation_1.1.0/sdk/automation/Azure.ResourceManager.Automation/) | | Resource Management - Azure AI Search | NuGet [1.2.3](https://www.nuget.org/packages/Azure.ResourceManager.Search/1.2.3)
NuGet [1.3.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Search/1.3.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Search-readme) | GitHub [1.2.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Search_1.2.3/sdk/search/Azure.ResourceManager.Search/)
GitHub [1.3.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Search_1.3.0-beta.4/sdk/search/Azure.ResourceManager.Search/) | @@ -244,24 +244,24 @@ | Resource Management - Fluid Relay | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.FluidRelay/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.FluidRelay-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.FluidRelay_1.1.0/sdk/fluidrelay/Azure.ResourceManager.FluidRelay/) | | Resource Management - Front Door | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.FrontDoor/1.3.1) | [docs](/dotnet/api/overview/azure/ResourceManager.FrontDoor-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.FrontDoor_1.3.1/sdk/frontdoor/Azure.ResourceManager.FrontDoor/) | | Resource Management - Graph Services | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.GraphServices/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.GraphServices-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.GraphServices_1.1.1/sdk/graphservices/Azure.ResourceManager.GraphServices/) | -| Resource Management - Guest Configuration | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.GuestConfiguration/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.GuestConfiguration-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.GuestConfiguration_1.2.0/sdk/guestconfiguration/Azure.ResourceManager.GuestConfiguration/) | +| Resource Management - Guest Configuration | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.GuestConfiguration/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.GuestConfiguration-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.GuestConfiguration_1.2.1/sdk/guestconfiguration/Azure.ResourceManager.GuestConfiguration/) | | Resource Management - Hardware Security Modules | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.HardwareSecurityModules/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.HardwareSecurityModules-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HardwareSecurityModules_1.0.0-beta.4/sdk/hardwaresecuritymodules/Azure.ResourceManager.HardwareSecurityModules/) | -| Resource Management - HDInsight | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.1.0)
NuGet [1.2.0-beta.3](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.2.0-beta.3) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.1.0/sdk/hdinsight/Azure.ResourceManager.HDInsight/)
GitHub [1.2.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.2.0-beta.3/sdk/hdinsight/Azure.ResourceManager.HDInsight/) | -| Resource Management - HDInsight Containers | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight.Containers/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight.Containers-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight.Containers_1.0.0-beta.4/sdk/hdinsightcontainers/Azure.ResourceManager.HDInsight.Containers/) | +| Resource Management - HDInsight | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.1.0)
NuGet [1.2.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.2.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.1.0/sdk/hdinsight/Azure.ResourceManager.HDInsight/)
GitHub [1.2.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.2.0-beta.4/sdk/hdinsight/Azure.ResourceManager.HDInsight/) | +| Resource Management - HDInsight Containers | NuGet [1.0.0-beta.5](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight.Containers/1.0.0-beta.5) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight.Containers-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.5](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight.Containers_1.0.0-beta.5/sdk/hdinsightcontainers/Azure.ResourceManager.HDInsight.Containers/) | | Resource Management - Health Bot | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.HealthBot/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthBot-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthBot_1.1.0/sdk/healthbot/Azure.ResourceManager.HealthBot/) | | Resource Management - Health Data AI Services | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HealthDataAIServices/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthDataAIServices-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthDataAIServices_1.0.0/sdk/healthdataaiservices/Azure.ResourceManager.HealthDataAIServices/) | -| Resource Management - Healthcare APIs | NuGet [1.3.0](https://www.nuget.org/packages/Azure.ResourceManager.HealthcareApis/1.3.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthcareApis-readme) | GitHub [1.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthcareApis_1.3.0/sdk/healthcareapis/Azure.ResourceManager.HealthcareApis/) | +| Resource Management - Healthcare APIs | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.HealthcareApis/1.3.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthcareApis-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthcareApis_1.3.1/sdk/healthcareapis/Azure.ResourceManager.HealthcareApis/) | | Resource Management - Hybrid Compute | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HybridCompute/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.HybridCompute/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridCompute-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridCompute_1.0.0/sdk/hybridcompute/Azure.ResourceManager.HybridCompute/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridCompute_1.1.0-beta.1/sdk/hybridcompute/Azure.ResourceManager.HybridCompute/) | | Resource Management - Hybrid Connectivity | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HybridConnectivity/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridConnectivity-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridConnectivity_1.0.0/sdk/hybridconnectivity/Azure.ResourceManager.HybridConnectivity/) | -| Resource Management - Hybrid Container Service | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HybridContainerService/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridContainerService-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridContainerService_1.0.0/sdk/hybridaks/Azure.ResourceManager.HybridContainerService/) | +| Resource Management - Hybrid Container Service | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.HybridContainerService/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridContainerService-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridContainerService_1.0.1/sdk/hybridaks/Azure.ResourceManager.HybridContainerService/) | | Resource Management - Hybrid Kubernetes | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Kubernetes/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Kubernetes-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Kubernetes_1.0.0-beta.4/sdk/hybridkubernetes/Azure.ResourceManager.Kubernetes/) | | Resource Management - Hybrid Network | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.HybridNetwork/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridNetwork-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridNetwork_1.0.0-beta.2/sdk/hybridnetwork/Azure.ResourceManager.HybridNetwork/) | | Resource Management - Informatica Data Management | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.InformaticaDataManagement/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.InformaticaDataManagement-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.InformaticaDataManagement_1.0.0-beta.1/sdk/informaticadatamanagement/Azure.ResourceManager.InformaticaDataManagement/) | | Resource Management - IoT Central | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.IotCentral/1.0.1)
NuGet [1.1.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.IotCentral/1.1.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.IotCentral-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotCentral_1.0.1/sdk/iotcentral/Azure.ResourceManager.IotCentral/)
GitHub [1.1.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotCentral_1.1.0-beta.2/sdk/iotcentral/Azure.ResourceManager.IotCentral/) | -| Resource Management - IoT Firmware Defense | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.IotFirmwareDefense/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.IotFirmwareDefense-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotFirmwareDefense_1.0.0/sdk/iot/Azure.ResourceManager.IotFirmwareDefense/) | +| Resource Management - IoT Firmware Defense | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.IotFirmwareDefense/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.IotFirmwareDefense-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotFirmwareDefense_1.0.1/sdk/iot/Azure.ResourceManager.IotFirmwareDefense/) | | Resource Management - IoT Hub | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.IotHub/1.1.1)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.IotHub/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.IotHub-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotHub_1.1.1/sdk/iothub/Azure.ResourceManager.IotHub/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotHub_1.2.0-beta.1/sdk/iothub/Azure.ResourceManager.IotHub/) | | Resource Management - Iotoperations | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.IotOperations/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.IotOperations-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotOperations_1.0.0/sdk/iotoperations/Azure.ResourceManager.IotOperations/) | -| Resource Management - Key Vault | NuGet [1.3.0](https://www.nuget.org/packages/Azure.ResourceManager.KeyVault/1.3.0) | [docs](/dotnet/api/overview/azure/ResourceManager.KeyVault-readme) | GitHub [1.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.KeyVault_1.3.0/sdk/keyvault/Azure.ResourceManager.KeyVault/) | +| Resource Management - Key Vault | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.KeyVault/1.3.1) | [docs](/dotnet/api/overview/azure/ResourceManager.KeyVault-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.KeyVault_1.3.1/sdk/keyvault/Azure.ResourceManager.KeyVault/) | | Resource Management - Kubernetes Configuration | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.KubernetesConfiguration/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.KubernetesConfiguration-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.KubernetesConfiguration_1.2.0/sdk/kubernetesconfiguration/Azure.ResourceManager.KubernetesConfiguration/) | | Resource Management - Kusto | NuGet [1.6.0](https://www.nuget.org/packages/Azure.ResourceManager.Kusto/1.6.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Kusto-readme) | GitHub [1.6.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Kusto_1.6.0/sdk/kusto/Azure.ResourceManager.Kusto/) | | Resource Management - Lab Services | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.LabServices/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.LabServices-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.LabServices_1.1.0/sdk/labservices/Azure.ResourceManager.LabServices/) | @@ -269,9 +269,9 @@ | Resource Management - Load Testing | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.LoadTesting/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.LoadTesting-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.LoadTesting_1.1.0/sdk/loadtestservice/Azure.ResourceManager.LoadTesting/) | | Resource Management - Log Analytics | NuGet [1.2.2](https://www.nuget.org/packages/Azure.ResourceManager.OperationalInsights/1.2.2)
NuGet [1.3.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.OperationalInsights/1.3.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.OperationalInsights-readme) | GitHub [1.2.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OperationalInsights_1.2.2/sdk/operationalinsights/Azure.ResourceManager.OperationalInsights/)
GitHub [1.3.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OperationalInsights_1.3.0-beta.1/sdk/operationalinsights/Azure.ResourceManager.OperationalInsights/) | | Resource Management - Logic Apps | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Logic/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Logic/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Logic-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Logic_1.1.0/sdk/logic/Azure.ResourceManager.Logic/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Logic_1.2.0-beta.1/sdk/logic/Azure.ResourceManager.Logic/) | -| Resource Management - Machine Learning | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.MachineLearning/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MachineLearning-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MachineLearning_1.2.1/sdk/machinelearningservices/Azure.ResourceManager.MachineLearning/) | +| Resource Management - Machine Learning | NuGet [1.2.2](https://www.nuget.org/packages/Azure.ResourceManager.MachineLearning/1.2.2) | [docs](/dotnet/api/overview/azure/ResourceManager.MachineLearning-readme) | GitHub [1.2.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MachineLearning_1.2.2/sdk/machinelearningservices/Azure.ResourceManager.MachineLearning/) | | Resource Management - Machine Learning Compute | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.MachineLearningCompute/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.MachineLearningCompute-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MachineLearningCompute_1.0.0-beta.4/sdk/machinelearningcompute/Azure.ResourceManager.MachineLearningCompute/) | -| Resource Management - Maintenance | NuGet [1.1.2](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.1.2)
NuGet [1.2.0-beta.8](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.2.0-beta.8) | [docs](/dotnet/api/overview/azure/ResourceManager.Maintenance-readme) | GitHub [1.1.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.1.2/sdk/maintenance/Azure.ResourceManager.Maintenance/)
GitHub [1.2.0-beta.8](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.2.0-beta.8/sdk/maintenance/Azure.ResourceManager.Maintenance/) | +| Resource Management - Maintenance | NuGet [1.1.2](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.1.2)
NuGet [1.2.0-beta.9](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.2.0-beta.9) | [docs](/dotnet/api/overview/azure/ResourceManager.Maintenance-readme) | GitHub [1.1.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.1.2/sdk/maintenance/Azure.ResourceManager.Maintenance/)
GitHub [1.2.0-beta.9](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.2.0-beta.9/sdk/maintenance/Azure.ResourceManager.Maintenance/) | | Resource Management - Managed Grafana | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Grafana/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Grafana-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Grafana_1.1.0/sdk/grafana/Azure.ResourceManager.Grafana/) | | Resource Management - Managed Network | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.ManagedNetwork/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.ManagedNetwork-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ManagedNetwork_1.0.0-beta.4/sdk/managednetwork/Azure.ResourceManager.ManagedNetwork/) | | Resource Management - Managed Network Fabric | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.ManagedNetworkFabric/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.ManagedNetworkFabric-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ManagedNetworkFabric_1.1.1/sdk/managednetworkfabric/Azure.ResourceManager.ManagedNetworkFabric/) | @@ -282,21 +282,21 @@ | Resource Management - Marketplace | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.Marketplace/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Marketplace-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Marketplace_1.1.1/sdk/marketplace/Azure.ResourceManager.Marketplace/) | | Resource Management - Marketplace Ordering | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.MarketplaceOrdering/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MarketplaceOrdering-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MarketplaceOrdering_1.1.0/sdk/marketplaceordering/Azure.ResourceManager.MarketplaceOrdering/) | | Resource Management - Media | NuGet [1.3.0](https://www.nuget.org/packages/Azure.ResourceManager.Media/1.3.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Media-readme) | GitHub [1.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Media_1.3.0/sdk/mediaservices/Azure.ResourceManager.Media/) | -| Resource Management - Migration Discovery SAP | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.MigrationDiscoverySap/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MigrationDiscoverySap-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MigrationDiscoverySap_1.0.0-beta.1/sdk/migrationdiscoverysap/Azure.ResourceManager.MigrationDiscoverySap/) | +| Resource Management - Migration Discovery SAP | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.MigrationDiscoverySap/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.MigrationDiscoverySap-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MigrationDiscoverySap_1.0.0-beta.2/sdk/migrationdiscoverysap/Azure.ResourceManager.MigrationDiscoverySap/) | | Resource Management - Mixed Reality | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.MixedReality/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MixedReality-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MixedReality_1.1.0/sdk/mixedreality/Azure.ResourceManager.MixedReality/) | | Resource Management - Mobile Network | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.MobileNetwork/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MobileNetwork-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MobileNetwork_1.2.0/sdk/mobilenetwork/Azure.ResourceManager.MobileNetwork/) | | Resource Management - Mongocluster | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.MongoCluster/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MongoCluster-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MongoCluster_1.0.0-beta.1/sdk/mongocluster/Azure.ResourceManager.MongoCluster/) | | Resource Management - Monitor | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.Monitor/1.3.1)
NuGet [1.4.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.Monitor/1.4.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.Monitor-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Monitor_1.3.1/sdk/monitor/Azure.ResourceManager.Monitor/)
GitHub [1.4.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Monitor_1.4.0-beta.2/sdk/monitor/Azure.ResourceManager.Monitor/) | -| Resource Management - MySQL | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.MySql/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MySql-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MySql_1.1.0/sdk/mysql/Azure.ResourceManager.MySql/) | +| Resource Management - MySQL | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.MySql/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MySql-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MySql_1.1.1/sdk/mysql/Azure.ResourceManager.MySql/) | | Resource Management - Neonpostgres | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.NeonPostgres/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NeonPostgres-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NeonPostgres_1.0.0-beta.1/sdk/neonpostgres/Azure.ResourceManager.NeonPostgres/) | | Resource Management - NetApp Files | NuGet [1.9.0](https://www.nuget.org/packages/Azure.ResourceManager.NetApp/1.9.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NetApp-readme) | GitHub [1.9.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetApp_1.9.0/sdk/netapp/Azure.ResourceManager.NetApp/) | | Resource Management - Network | NuGet [1.10.0](https://www.nuget.org/packages/Azure.ResourceManager.Network/1.10.0)
NuGet [1.11.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Network/1.11.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Network-readme) | GitHub [1.10.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Network_1.10.0/sdk/network/Azure.ResourceManager.Network/)
GitHub [1.11.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Network_1.11.0-beta.1/sdk/network/Azure.ResourceManager.Network/) | | Resource Management - Network Cloud | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.NetworkCloud/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NetworkCloud-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetworkCloud_1.1.0/sdk/networkcloud/Azure.ResourceManager.NetworkCloud/) | | Resource Management - Network Function | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.NetworkFunction/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.NetworkFunction-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetworkFunction_1.0.0-beta.4/sdk/networkfunction/Azure.ResourceManager.NetworkFunction/) | -| Resource Management - New Relic Observability | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.NewRelicObservability/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NewRelicObservability-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NewRelicObservability_1.1.0/sdk/newrelicobservability/Azure.ResourceManager.NewRelicObservability/) | +| Resource Management - New Relic Observability | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.NewRelicObservability/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NewRelicObservability-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NewRelicObservability_1.1.1/sdk/newrelicobservability/Azure.ResourceManager.NewRelicObservability/) | | Resource Management - Nginx | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.Nginx/1.0.0)
NuGet [1.1.0-beta.3](https://www.nuget.org/packages/Azure.ResourceManager.Nginx/1.1.0-beta.3) | [docs](/dotnet/api/overview/azure/ResourceManager.Nginx-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Nginx_1.0.0/sdk/nginx/Azure.ResourceManager.Nginx/)
GitHub [1.1.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Nginx_1.1.0-beta.3/sdk/nginx/Azure.ResourceManager.Nginx/) | -| Resource Management - Notification Hubs | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NotificationHubs-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.1.0/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.2.0-beta.1/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/) | -| Resource Management - Oracle Database | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.OracleDatabase/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.OracleDatabase-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OracleDatabase_1.0.0/sdk/oracle/Azure.ResourceManager.OracleDatabase/) | +| Resource Management - Notification Hubs | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.1.1)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NotificationHubs-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.1.1/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.2.0-beta.1/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/) | +| Resource Management - Oracle Database | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.OracleDatabase/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.OracleDatabase-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OracleDatabase_1.0.1/sdk/oracle/Azure.ResourceManager.OracleDatabase/) | | Resource Management - Orbital | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Orbital/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Orbital-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Orbital_1.1.0/sdk/orbital/Azure.ResourceManager.Orbital/) | | Resource Management - Palo Alto Networks - Next Generation Firewall | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.PaloAltoNetworks.Ngfw/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.PaloAltoNetworks.Ngfw-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.PaloAltoNetworks.Ngfw_1.1.0/sdk/paloaltonetworks.ngfw/Azure.ResourceManager.PaloAltoNetworks.Ngfw/) | | Resource Management - Peering | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.Peering/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Peering-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Peering_1.2.1/sdk/peering/Azure.ResourceManager.Peering/) | @@ -306,16 +306,16 @@ | Resource Management - Power BI Dedicated | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.PowerBIDedicated/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.PowerBIDedicated-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.PowerBIDedicated_1.0.0-beta.4/sdk/powerbidedicated/Azure.ResourceManager.PowerBIDedicated/) | | Resource Management - Private DNS | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.PrivateDns/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.PrivateDns-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.PrivateDns_1.2.0/sdk/privatedns/Azure.ResourceManager.PrivateDns/) | | Resource Management - Provider Hub | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.ProviderHub/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.ProviderHub-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ProviderHub_1.1.0/sdk/providerhub/Azure.ResourceManager.ProviderHub/) | -| Resource Management - Purview | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Purview-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.1.0/sdk/purview/Azure.ResourceManager.Purview/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.2.0-beta.1/sdk/purview/Azure.ResourceManager.Purview/) | -| Resource Management - Quantum | NuGet [1.0.0-beta.5](https://www.nuget.org/packages/Azure.ResourceManager.Quantum/1.0.0-beta.5) | [docs](/dotnet/api/overview/azure/ResourceManager.Quantum-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.5](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Quantum_1.0.0-beta.5/sdk/quantum/Azure.ResourceManager.Quantum/) | -| Resource Management - Qumulo | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Qumulo/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Qumulo-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Qumulo_1.1.0/sdk/qumulo/Azure.ResourceManager.Qumulo/) | +| Resource Management - Purview | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.1.0)
NuGet [1.2.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.2.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.Purview-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.1.0/sdk/purview/Azure.ResourceManager.Purview/)
GitHub [1.2.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.2.0-beta.2/sdk/purview/Azure.ResourceManager.Purview/) | +| Resource Management - Quantum | NuGet [1.0.0-beta.6](https://www.nuget.org/packages/Azure.ResourceManager.Quantum/1.0.0-beta.6) | [docs](/dotnet/api/overview/azure/ResourceManager.Quantum-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.6](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Quantum_1.0.0-beta.6/sdk/quantum/Azure.ResourceManager.Quantum/) | +| Resource Management - Qumulo | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.Qumulo/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Qumulo-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Qumulo_1.1.1/sdk/qumulo/Azure.ResourceManager.Qumulo/) | | Resource Management - Quota | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Quota/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Quota-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Quota_1.1.0/sdk/quota/Azure.ResourceManager.Quota/) | | Resource Management - Recovery Services | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServices/1.1.1)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServices/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServices-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServices_1.1.1/sdk/recoveryservices/Azure.ResourceManager.RecoveryServices/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServices_1.2.0-beta.1/sdk/recoveryservices/Azure.ResourceManager.RecoveryServices/) | | Resource Management - Recovery Services Backup | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesBackup/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesBackup-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesBackup_1.2.0/sdk/recoveryservices-backup/Azure.ResourceManager.RecoveryServicesBackup/) | | Resource Management - Recovery Services Data Replication | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesDataReplication/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesDataReplication-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesDataReplication_1.0.0-beta.2/sdk/recoveryservices-datareplication/Azure.ResourceManager.RecoveryServicesDataReplication/) | -| Resource Management - Recovery Services Site Recovery | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesSiteRecovery/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesSiteRecovery-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesSiteRecovery_1.2.0/sdk/recoveryservices-siterecovery/Azure.ResourceManager.RecoveryServicesSiteRecovery/) | +| Resource Management - Recovery Services Site Recovery | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesSiteRecovery/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesSiteRecovery-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesSiteRecovery_1.2.1/sdk/recoveryservices-siterecovery/Azure.ResourceManager.RecoveryServicesSiteRecovery/) | | Resource Management - Redis | NuGet [1.5.0](https://www.nuget.org/packages/Azure.ResourceManager.Redis/1.5.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Redis-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Redis_1.5.0/sdk/redis/Azure.ResourceManager.Redis/) | -| Resource Management - Redis Enterprise | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RedisEnterprise-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.1.0/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.2.0-beta.1/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/) | +| Resource Management - Redis Enterprise | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.1.0)
NuGet [1.2.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.2.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.RedisEnterprise-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.1.0/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/)
GitHub [1.2.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.2.0-beta.2/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/) | | Resource Management - Redis Enterprise Cache | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterpriseCache/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RedisEnterpriseCache-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterpriseCache_1.0.0-beta.1/sdk/redisenterprise/Azure.ResourceManager.RedisEnterpriseCache/) | | Resource Management - Relay | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.Relay/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Relay-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Relay_1.2.0/sdk/relay/Azure.ResourceManager.Relay/) | | Resource Management - Reservations | NuGet [1.4.0](https://www.nuget.org/packages/Azure.ResourceManager.Reservations/1.4.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Reservations-readme) | GitHub [1.4.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Reservations_1.4.0/sdk/reservations/Azure.ResourceManager.Reservations/) | @@ -361,7 +361,7 @@ | App Configuration Extension | NuGet [8.1.1](https://www.nuget.org/packages/Microsoft.Azure.AppConfiguration.Functions.Worker/8.1.1) | | | | App Configuration Provider | NuGet [8.1.1](https://www.nuget.org/packages/Microsoft.Azure.AppConfiguration.AspNetCore/8.1.1) | | | | Azure.Communication.Administration | NuGet [1.0.0-beta.3](https://www.nuget.org/packages/Azure.Communication.Administration/1.0.0-beta.3) | | | -| Communication Calling Windows Client | NuGet [1.11.1](https://www.nuget.org/packages/Azure.Communication.Calling.WindowsClient/1.11.1) | | | +| Communication Calling Windows Client | NuGet [1.11.1](https://www.nuget.org/packages/Azure.Communication.Calling.WindowsClient/1.11.1)
NuGet [1.12.0-beta.1](https://www.nuget.org/packages/Azure.Communication.Calling.WindowsClient/1.12.0-beta.1) | | | | DotNetty | NuGet [0.7.6](https://www.nuget.org/packages/DotNetty.Common/0.7.6) | | | | HTTP ASPNETCore Analyzers | NuGet [1.0.3](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers/1.0.3) | | | | Item Templates NetCore | NuGet [4.0.5051](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.ItemTemplates.NetCore/4.0.5051) | | | diff --git a/docs/azure/includes/dotnet-new.md b/docs/azure/includes/dotnet-new.md index c15de236f6293..cfd430a49e447 100644 --- a/docs/azure/includes/dotnet-new.md +++ b/docs/azure/includes/dotnet-new.md @@ -181,7 +181,7 @@ | Resource Management - Arc ScVmm | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.ArcScVmm/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.ArcScVmm-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ArcScVmm_1.0.0-beta.4/sdk/arc-scvmm/Azure.ResourceManager.ArcScVmm/) | | Resource Management - Astro | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.Astro/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.Astro-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Astro_1.0.0-beta.2/sdk/astronomer/Azure.ResourceManager.Astro/) | | Resource Management - Attestation | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Attestation/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Attestation-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Attestation_1.0.0-beta.4/sdk/attestation/Azure.ResourceManager.Attestation/) | -| Resource Management - Authorization | NuGet [1.1.3](https://www.nuget.org/packages/Azure.ResourceManager.Authorization/1.1.3) | [docs](/dotnet/api/overview/azure/ResourceManager.Authorization-readme) | GitHub [1.1.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Authorization_1.1.3/sdk/authorization/Azure.ResourceManager.Authorization/) | +| Resource Management - Authorization | NuGet [1.1.4](https://www.nuget.org/packages/Azure.ResourceManager.Authorization/1.1.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Authorization-readme) | GitHub [1.1.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Authorization_1.1.4/sdk/authorization/Azure.ResourceManager.Authorization/) | | Resource Management - Automanage | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Automanage/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Automanage-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Automanage_1.1.0/sdk/automanage/Azure.ResourceManager.Automanage/) | | Resource Management - Automation | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Automation/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Automation-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Automation_1.1.0/sdk/automation/Azure.ResourceManager.Automation/) | | Resource Management - Azure AI Search | NuGet [1.2.3](https://www.nuget.org/packages/Azure.ResourceManager.Search/1.2.3)
NuGet [1.3.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Search/1.3.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Search-readme) | GitHub [1.2.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Search_1.2.3/sdk/search/Azure.ResourceManager.Search/)
GitHub [1.3.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Search_1.3.0-beta.4/sdk/search/Azure.ResourceManager.Search/) | @@ -250,25 +250,25 @@ | Resource Management - Fluid Relay | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.FluidRelay/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.FluidRelay-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.FluidRelay_1.1.0/sdk/fluidrelay/Azure.ResourceManager.FluidRelay/) | | Resource Management - Front Door | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.FrontDoor/1.3.1) | [docs](/dotnet/api/overview/azure/ResourceManager.FrontDoor-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.FrontDoor_1.3.1/sdk/frontdoor/Azure.ResourceManager.FrontDoor/) | | Resource Management - Graph Services | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.GraphServices/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.GraphServices-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.GraphServices_1.1.1/sdk/graphservices/Azure.ResourceManager.GraphServices/) | -| Resource Management - Guest Configuration | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.GuestConfiguration/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.GuestConfiguration-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.GuestConfiguration_1.2.0/sdk/guestconfiguration/Azure.ResourceManager.GuestConfiguration/) | +| Resource Management - Guest Configuration | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.GuestConfiguration/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.GuestConfiguration-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.GuestConfiguration_1.2.1/sdk/guestconfiguration/Azure.ResourceManager.GuestConfiguration/) | | Resource Management - Hardware Security Modules | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.HardwareSecurityModules/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.HardwareSecurityModules-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HardwareSecurityModules_1.0.0-beta.4/sdk/hardwaresecuritymodules/Azure.ResourceManager.HardwareSecurityModules/) | -| Resource Management - HDInsight | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.1.0)
NuGet [1.2.0-beta.3](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.2.0-beta.3) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.1.0/sdk/hdinsight/Azure.ResourceManager.HDInsight/)
GitHub [1.2.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.2.0-beta.3/sdk/hdinsight/Azure.ResourceManager.HDInsight/) | -| Resource Management - HDInsight Containers | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight.Containers/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight.Containers-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight.Containers_1.0.0-beta.4/sdk/hdinsightcontainers/Azure.ResourceManager.HDInsight.Containers/) | +| Resource Management - HDInsight | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.1.0)
NuGet [1.2.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight/1.2.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.1.0/sdk/hdinsight/Azure.ResourceManager.HDInsight/)
GitHub [1.2.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight_1.2.0-beta.4/sdk/hdinsight/Azure.ResourceManager.HDInsight/) | +| Resource Management - HDInsight Containers | NuGet [1.0.0-beta.5](https://www.nuget.org/packages/Azure.ResourceManager.HDInsight.Containers/1.0.0-beta.5) | [docs](/dotnet/api/overview/azure/ResourceManager.HDInsight.Containers-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.5](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HDInsight.Containers_1.0.0-beta.5/sdk/hdinsightcontainers/Azure.ResourceManager.HDInsight.Containers/) | | Resource Management - Health Bot | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.HealthBot/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthBot-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthBot_1.1.0/sdk/healthbot/Azure.ResourceManager.HealthBot/) | | Resource Management - Health Data AI Services | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HealthDataAIServices/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthDataAIServices-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthDataAIServices_1.0.0/sdk/healthdataaiservices/Azure.ResourceManager.HealthDataAIServices/) | -| Resource Management - Healthcare APIs | NuGet [1.3.0](https://www.nuget.org/packages/Azure.ResourceManager.HealthcareApis/1.3.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthcareApis-readme) | GitHub [1.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthcareApis_1.3.0/sdk/healthcareapis/Azure.ResourceManager.HealthcareApis/) | +| Resource Management - Healthcare APIs | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.HealthcareApis/1.3.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HealthcareApis-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HealthcareApis_1.3.1/sdk/healthcareapis/Azure.ResourceManager.HealthcareApis/) | | Resource Management - Hybrid Compute | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HybridCompute/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.HybridCompute/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridCompute-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridCompute_1.0.0/sdk/hybridcompute/Azure.ResourceManager.HybridCompute/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridCompute_1.1.0-beta.1/sdk/hybridcompute/Azure.ResourceManager.HybridCompute/) | | Resource Management - Hybrid Connectivity | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HybridConnectivity/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridConnectivity-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridConnectivity_1.0.0/sdk/hybridconnectivity/Azure.ResourceManager.HybridConnectivity/) | -| Resource Management - Hybrid Container Service | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.HybridContainerService/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridContainerService-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridContainerService_1.0.0/sdk/hybridaks/Azure.ResourceManager.HybridContainerService/) | +| Resource Management - Hybrid Container Service | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.HybridContainerService/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridContainerService-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridContainerService_1.0.1/sdk/hybridaks/Azure.ResourceManager.HybridContainerService/) | | Resource Management - Hybrid Data | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.HybridData/1.0.1)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.HybridData/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridData-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridData_1.1.0-beta.1/sdk/hybriddatamanager/Azure.ResourceManager.HybridData) | | Resource Management - Hybrid Kubernetes | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.Kubernetes/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.Kubernetes-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Kubernetes_1.0.0-beta.4/sdk/hybridkubernetes/Azure.ResourceManager.Kubernetes/) | | Resource Management - Hybrid Network | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.HybridNetwork/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.HybridNetwork-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.HybridNetwork_1.0.0-beta.2/sdk/hybridnetwork/Azure.ResourceManager.HybridNetwork/) | | Resource Management - Informatica Data Management | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.InformaticaDataManagement/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.InformaticaDataManagement-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.InformaticaDataManagement_1.0.0-beta.1/sdk/informaticadatamanagement/Azure.ResourceManager.InformaticaDataManagement/) | | Resource Management - IoT Central | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.IotCentral/1.0.1)
NuGet [1.1.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.IotCentral/1.1.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.IotCentral-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotCentral_1.0.1/sdk/iotcentral/Azure.ResourceManager.IotCentral/)
GitHub [1.1.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotCentral_1.1.0-beta.2/sdk/iotcentral/Azure.ResourceManager.IotCentral/) | -| Resource Management - IoT Firmware Defense | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.IotFirmwareDefense/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.IotFirmwareDefense-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotFirmwareDefense_1.0.0/sdk/iot/Azure.ResourceManager.IotFirmwareDefense/) | +| Resource Management - IoT Firmware Defense | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.IotFirmwareDefense/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.IotFirmwareDefense-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotFirmwareDefense_1.0.1/sdk/iot/Azure.ResourceManager.IotFirmwareDefense/) | | Resource Management - IoT Hub | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.IotHub/1.1.1)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.IotHub/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.IotHub-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotHub_1.1.1/sdk/iothub/Azure.ResourceManager.IotHub/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotHub_1.2.0-beta.1/sdk/iothub/Azure.ResourceManager.IotHub/) | | Resource Management - Iotoperations | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.IotOperations/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.IotOperations-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.IotOperations_1.0.0/sdk/iotoperations/Azure.ResourceManager.IotOperations/) | -| Resource Management - Key Vault | NuGet [1.3.0](https://www.nuget.org/packages/Azure.ResourceManager.KeyVault/1.3.0) | [docs](/dotnet/api/overview/azure/ResourceManager.KeyVault-readme) | GitHub [1.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.KeyVault_1.3.0/sdk/keyvault/Azure.ResourceManager.KeyVault/) | +| Resource Management - Key Vault | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.KeyVault/1.3.1) | [docs](/dotnet/api/overview/azure/ResourceManager.KeyVault-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.KeyVault_1.3.1/sdk/keyvault/Azure.ResourceManager.KeyVault/) | | Resource Management - Kubernetes Configuration | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.KubernetesConfiguration/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.KubernetesConfiguration-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.KubernetesConfiguration_1.2.0/sdk/kubernetesconfiguration/Azure.ResourceManager.KubernetesConfiguration/) | | Resource Management - Kusto | NuGet [1.6.0](https://www.nuget.org/packages/Azure.ResourceManager.Kusto/1.6.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Kusto-readme) | GitHub [1.6.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Kusto_1.6.0/sdk/kusto/Azure.ResourceManager.Kusto/) | | Resource Management - Lab Services | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.LabServices/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.LabServices-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.LabServices_1.1.0/sdk/labservices/Azure.ResourceManager.LabServices/) | @@ -276,9 +276,9 @@ | Resource Management - Load Testing | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.LoadTesting/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.LoadTesting-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.LoadTesting_1.1.0/sdk/loadtestservice/Azure.ResourceManager.LoadTesting/) | | Resource Management - Log Analytics | NuGet [1.2.2](https://www.nuget.org/packages/Azure.ResourceManager.OperationalInsights/1.2.2)
NuGet [1.3.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.OperationalInsights/1.3.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.OperationalInsights-readme) | GitHub [1.2.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OperationalInsights_1.2.2/sdk/operationalinsights/Azure.ResourceManager.OperationalInsights/)
GitHub [1.3.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OperationalInsights_1.3.0-beta.1/sdk/operationalinsights/Azure.ResourceManager.OperationalInsights/) | | Resource Management - Logic Apps | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Logic/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Logic/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Logic-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Logic_1.1.0/sdk/logic/Azure.ResourceManager.Logic/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Logic_1.2.0-beta.1/sdk/logic/Azure.ResourceManager.Logic/) | -| Resource Management - Machine Learning | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.MachineLearning/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MachineLearning-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MachineLearning_1.2.1/sdk/machinelearningservices/Azure.ResourceManager.MachineLearning/) | +| Resource Management - Machine Learning | NuGet [1.2.2](https://www.nuget.org/packages/Azure.ResourceManager.MachineLearning/1.2.2) | [docs](/dotnet/api/overview/azure/ResourceManager.MachineLearning-readme) | GitHub [1.2.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MachineLearning_1.2.2/sdk/machinelearningservices/Azure.ResourceManager.MachineLearning/) | | Resource Management - Machine Learning Compute | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.MachineLearningCompute/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.MachineLearningCompute-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MachineLearningCompute_1.0.0-beta.4/sdk/machinelearningcompute/Azure.ResourceManager.MachineLearningCompute/) | -| Resource Management - Maintenance | NuGet [1.1.2](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.1.2)
NuGet [1.2.0-beta.8](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.2.0-beta.8) | [docs](/dotnet/api/overview/azure/ResourceManager.Maintenance-readme) | GitHub [1.1.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.1.2/sdk/maintenance/Azure.ResourceManager.Maintenance/)
GitHub [1.2.0-beta.8](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.2.0-beta.8/sdk/maintenance/Azure.ResourceManager.Maintenance/) | +| Resource Management - Maintenance | NuGet [1.1.2](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.1.2)
NuGet [1.2.0-beta.9](https://www.nuget.org/packages/Azure.ResourceManager.Maintenance/1.2.0-beta.9) | [docs](/dotnet/api/overview/azure/ResourceManager.Maintenance-readme) | GitHub [1.1.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.1.2/sdk/maintenance/Azure.ResourceManager.Maintenance/)
GitHub [1.2.0-beta.9](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Maintenance_1.2.0-beta.9/sdk/maintenance/Azure.ResourceManager.Maintenance/) | | Resource Management - Managed Grafana | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Grafana/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Grafana-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Grafana_1.1.0/sdk/grafana/Azure.ResourceManager.Grafana/) | | Resource Management - Managed Network | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.ManagedNetwork/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.ManagedNetwork-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ManagedNetwork_1.0.0-beta.4/sdk/managednetwork/Azure.ResourceManager.ManagedNetwork/) | | Resource Management - Managed Network Fabric | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.ManagedNetworkFabric/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.ManagedNetworkFabric-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ManagedNetworkFabric_1.1.1/sdk/managednetworkfabric/Azure.ResourceManager.ManagedNetworkFabric/) | @@ -289,22 +289,22 @@ | Resource Management - Marketplace | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.Marketplace/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Marketplace-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Marketplace_1.1.1/sdk/marketplace/Azure.ResourceManager.Marketplace/) | | Resource Management - Marketplace Ordering | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.MarketplaceOrdering/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MarketplaceOrdering-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MarketplaceOrdering_1.1.0/sdk/marketplaceordering/Azure.ResourceManager.MarketplaceOrdering/) | | Resource Management - Media | NuGet [1.3.0](https://www.nuget.org/packages/Azure.ResourceManager.Media/1.3.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Media-readme) | GitHub [1.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Media_1.3.0/sdk/mediaservices/Azure.ResourceManager.Media/) | -| Resource Management - Migration Discovery SAP | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.MigrationDiscoverySap/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MigrationDiscoverySap-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MigrationDiscoverySap_1.0.0-beta.1/sdk/migrationdiscoverysap/Azure.ResourceManager.MigrationDiscoverySap/) | +| Resource Management - Migration Discovery SAP | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.MigrationDiscoverySap/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.MigrationDiscoverySap-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MigrationDiscoverySap_1.0.0-beta.2/sdk/migrationdiscoverysap/Azure.ResourceManager.MigrationDiscoverySap/) | | Resource Management - Mixed Reality | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.MixedReality/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MixedReality-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MixedReality_1.1.0/sdk/mixedreality/Azure.ResourceManager.MixedReality/) | | Resource Management - Mobile Network | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.MobileNetwork/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MobileNetwork-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MobileNetwork_1.2.0/sdk/mobilenetwork/Azure.ResourceManager.MobileNetwork/) | | Resource Management - Mongocluster | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.MongoCluster/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MongoCluster-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MongoCluster_1.0.0-beta.1/sdk/mongocluster/Azure.ResourceManager.MongoCluster/) | | Resource Management - Monitor | NuGet [1.3.1](https://www.nuget.org/packages/Azure.ResourceManager.Monitor/1.3.1)
NuGet [1.4.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.Monitor/1.4.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.Monitor-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Monitor_1.3.1/sdk/monitor/Azure.ResourceManager.Monitor/)
GitHub [1.4.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Monitor_1.4.0-beta.2/sdk/monitor/Azure.ResourceManager.Monitor/) | -| Resource Management - MySQL | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.MySql/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.MySql-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MySql_1.1.0/sdk/mysql/Azure.ResourceManager.MySql/) | +| Resource Management - MySQL | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.MySql/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.MySql-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.MySql_1.1.1/sdk/mysql/Azure.ResourceManager.MySql/) | | Resource Management - Neonpostgres | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.NeonPostgres/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NeonPostgres-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NeonPostgres_1.0.0-beta.1/sdk/neonpostgres/Azure.ResourceManager.NeonPostgres/) | | Resource Management - NetApp Files | NuGet [1.9.0](https://www.nuget.org/packages/Azure.ResourceManager.NetApp/1.9.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NetApp-readme) | GitHub [1.9.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetApp_1.9.0/sdk/netapp/Azure.ResourceManager.NetApp/) | | Resource Management - Network | NuGet [1.10.0](https://www.nuget.org/packages/Azure.ResourceManager.Network/1.10.0)
NuGet [1.11.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Network/1.11.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Network-readme) | GitHub [1.10.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Network_1.10.0/sdk/network/Azure.ResourceManager.Network/)
GitHub [1.11.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Network_1.11.0-beta.1/sdk/network/Azure.ResourceManager.Network/) | | Resource Management - Network Analytics | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.NetworkAnalytics/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NetworkAnalytics-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetworkAnalytics_1.0.0/sdk/networkanalytics/Azure.ResourceManager.NetworkAnalytics/) | | Resource Management - Network Cloud | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.NetworkCloud/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NetworkCloud-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetworkCloud_1.1.0/sdk/networkcloud/Azure.ResourceManager.NetworkCloud/) | | Resource Management - Network Function | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.NetworkFunction/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.NetworkFunction-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NetworkFunction_1.0.0-beta.4/sdk/networkfunction/Azure.ResourceManager.NetworkFunction/) | -| Resource Management - New Relic Observability | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.NewRelicObservability/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.NewRelicObservability-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NewRelicObservability_1.1.0/sdk/newrelicobservability/Azure.ResourceManager.NewRelicObservability/) | +| Resource Management - New Relic Observability | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.NewRelicObservability/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NewRelicObservability-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NewRelicObservability_1.1.1/sdk/newrelicobservability/Azure.ResourceManager.NewRelicObservability/) | | Resource Management - Nginx | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.Nginx/1.0.0)
NuGet [1.1.0-beta.3](https://www.nuget.org/packages/Azure.ResourceManager.Nginx/1.1.0-beta.3) | [docs](/dotnet/api/overview/azure/ResourceManager.Nginx-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Nginx_1.0.0/sdk/nginx/Azure.ResourceManager.Nginx/)
GitHub [1.1.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Nginx_1.1.0-beta.3/sdk/nginx/Azure.ResourceManager.Nginx/) | -| Resource Management - Notification Hubs | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NotificationHubs-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.1.0/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.2.0-beta.1/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/) | -| Resource Management - Oracle Database | NuGet [1.0.0](https://www.nuget.org/packages/Azure.ResourceManager.OracleDatabase/1.0.0) | [docs](/dotnet/api/overview/azure/ResourceManager.OracleDatabase-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OracleDatabase_1.0.0/sdk/oracle/Azure.ResourceManager.OracleDatabase/) | +| Resource Management - Notification Hubs | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.1.1)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.NotificationHubs/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.NotificationHubs-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.1.1/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.NotificationHubs_1.2.0-beta.1/sdk/notificationhubs/Azure.ResourceManager.NotificationHubs/) | +| Resource Management - Oracle Database | NuGet [1.0.1](https://www.nuget.org/packages/Azure.ResourceManager.OracleDatabase/1.0.1) | [docs](/dotnet/api/overview/azure/ResourceManager.OracleDatabase-readme) | GitHub [1.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.OracleDatabase_1.0.1/sdk/oracle/Azure.ResourceManager.OracleDatabase/) | | Resource Management - Orbital | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Orbital/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Orbital-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Orbital_1.1.0/sdk/orbital/Azure.ResourceManager.Orbital/) | | Resource Management - Palo Alto Networks - Next Generation Firewall | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.PaloAltoNetworks.Ngfw/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.PaloAltoNetworks.Ngfw-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.PaloAltoNetworks.Ngfw_1.1.0/sdk/paloaltonetworks.ngfw/Azure.ResourceManager.PaloAltoNetworks.Ngfw/) | | Resource Management - Peering | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.Peering/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Peering-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Peering_1.2.1/sdk/peering/Azure.ResourceManager.Peering/) | @@ -314,16 +314,16 @@ | Resource Management - Power BI Dedicated | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.ResourceManager.PowerBIDedicated/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/ResourceManager.PowerBIDedicated-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.PowerBIDedicated_1.0.0-beta.4/sdk/powerbidedicated/Azure.ResourceManager.PowerBIDedicated/) | | Resource Management - Private DNS | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.PrivateDns/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.PrivateDns-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.PrivateDns_1.2.0/sdk/privatedns/Azure.ResourceManager.PrivateDns/) | | Resource Management - Provider Hub | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.ProviderHub/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.ProviderHub-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.ProviderHub_1.1.0/sdk/providerhub/Azure.ResourceManager.ProviderHub/) | -| Resource Management - Purview | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Purview-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.1.0/sdk/purview/Azure.ResourceManager.Purview/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.2.0-beta.1/sdk/purview/Azure.ResourceManager.Purview/) | -| Resource Management - Quantum | NuGet [1.0.0-beta.5](https://www.nuget.org/packages/Azure.ResourceManager.Quantum/1.0.0-beta.5) | [docs](/dotnet/api/overview/azure/ResourceManager.Quantum-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.5](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Quantum_1.0.0-beta.5/sdk/quantum/Azure.ResourceManager.Quantum/) | -| Resource Management - Qumulo | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Qumulo/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Qumulo-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Qumulo_1.1.0/sdk/qumulo/Azure.ResourceManager.Qumulo/) | +| Resource Management - Purview | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.1.0)
NuGet [1.2.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.Purview/1.2.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.Purview-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.1.0/sdk/purview/Azure.ResourceManager.Purview/)
GitHub [1.2.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Purview_1.2.0-beta.2/sdk/purview/Azure.ResourceManager.Purview/) | +| Resource Management - Quantum | NuGet [1.0.0-beta.6](https://www.nuget.org/packages/Azure.ResourceManager.Quantum/1.0.0-beta.6) | [docs](/dotnet/api/overview/azure/ResourceManager.Quantum-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.6](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Quantum_1.0.0-beta.6/sdk/quantum/Azure.ResourceManager.Quantum/) | +| Resource Management - Qumulo | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.Qumulo/1.1.1) | [docs](/dotnet/api/overview/azure/ResourceManager.Qumulo-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Qumulo_1.1.1/sdk/qumulo/Azure.ResourceManager.Qumulo/) | | Resource Management - Quota | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.Quota/1.1.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Quota-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Quota_1.1.0/sdk/quota/Azure.ResourceManager.Quota/) | | Resource Management - Recovery Services | NuGet [1.1.1](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServices/1.1.1)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServices/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServices-readme) | GitHub [1.1.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServices_1.1.1/sdk/recoveryservices/Azure.ResourceManager.RecoveryServices/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServices_1.2.0-beta.1/sdk/recoveryservices/Azure.ResourceManager.RecoveryServices/) | | Resource Management - Recovery Services Backup | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesBackup/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesBackup-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesBackup_1.2.0/sdk/recoveryservices-backup/Azure.ResourceManager.RecoveryServicesBackup/) | | Resource Management - Recovery Services Data Replication | NuGet [1.0.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesDataReplication/1.0.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesDataReplication-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesDataReplication_1.0.0-beta.2/sdk/recoveryservices-datareplication/Azure.ResourceManager.RecoveryServicesDataReplication/) | -| Resource Management - Recovery Services Site Recovery | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesSiteRecovery/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesSiteRecovery-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesSiteRecovery_1.2.0/sdk/recoveryservices-siterecovery/Azure.ResourceManager.RecoveryServicesSiteRecovery/) | +| Resource Management - Recovery Services Site Recovery | NuGet [1.2.1](https://www.nuget.org/packages/Azure.ResourceManager.RecoveryServicesSiteRecovery/1.2.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RecoveryServicesSiteRecovery-readme) | GitHub [1.2.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RecoveryServicesSiteRecovery_1.2.1/sdk/recoveryservices-siterecovery/Azure.ResourceManager.RecoveryServicesSiteRecovery/) | | Resource Management - Redis | NuGet [1.5.0](https://www.nuget.org/packages/Azure.ResourceManager.Redis/1.5.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Redis-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Redis_1.5.0/sdk/redis/Azure.ResourceManager.Redis/) | -| Resource Management - Redis Enterprise | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.1.0)
NuGet [1.2.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.2.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RedisEnterprise-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.1.0/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/)
GitHub [1.2.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.2.0-beta.1/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/) | +| Resource Management - Redis Enterprise | NuGet [1.1.0](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.1.0)
NuGet [1.2.0-beta.2](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterprise/1.2.0-beta.2) | [docs](/dotnet/api/overview/azure/ResourceManager.RedisEnterprise-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.1.0/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/)
GitHub [1.2.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterprise_1.2.0-beta.2/sdk/redisenterprise/Azure.ResourceManager.RedisEnterprise/) | | Resource Management - Redis Enterprise Cache | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.ResourceManager.RedisEnterpriseCache/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/ResourceManager.RedisEnterpriseCache-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.RedisEnterpriseCache_1.0.0-beta.1/sdk/redisenterprise/Azure.ResourceManager.RedisEnterpriseCache/) | | Resource Management - Relay | NuGet [1.2.0](https://www.nuget.org/packages/Azure.ResourceManager.Relay/1.2.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Relay-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Relay_1.2.0/sdk/relay/Azure.ResourceManager.Relay/) | | Resource Management - Reservations | NuGet [1.4.0](https://www.nuget.org/packages/Azure.ResourceManager.Reservations/1.4.0) | [docs](/dotnet/api/overview/azure/ResourceManager.Reservations-readme) | GitHub [1.4.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.ResourceManager.Reservations_1.4.0/sdk/reservations/Azure.ResourceManager.Reservations/) | diff --git a/docs/azure/media/group-role-assignment.png b/docs/azure/media/group-role-assignment.png new file mode 100644 index 0000000000000..b684b45960d83 Binary files /dev/null and b/docs/azure/media/group-role-assignment.png differ diff --git a/docs/azure/sdk/authentication/local-development-service-principal.md b/docs/azure/sdk/authentication/local-development-service-principal.md index 1654c86a90410..7bf16926d7f43 100644 --- a/docs/azure/sdk/authentication/local-development-service-principal.md +++ b/docs/azure/sdk/authentication/local-development-service-principal.md @@ -3,7 +3,7 @@ title: Authenticate .NET apps to Azure services during local development using s description: Learn how to authenticate your app to Azure services during local development using dedicated application service principals. ms.topic: how-to ms.custom: devx-track-dotnet, engagement-fy23, devx-track-azurecli -ms.date: 03/04/2025 +ms.date: 03/11/2025 --- # Authenticate .NET apps to Azure services during local development using service principals @@ -26,252 +26,12 @@ When the app is registered in Azure, an application service principal is created During local development, environment variables are set with the application service principal's identity. The Azure Identity library reads these environment variables to authenticate the app to the required Azure resources. -## Register the app in Azure +[!INCLUDE [create-app-registration](../includes/auth-create-app-registration.md)] -Application service principal objects are created through an app registration in Azure using either the Azure portal or Azure CLI. +[!INCLUDE [create-entra-group](../includes/auth-create-entra-group.md)] -### [Azure portal](#tab/azure-portal) +[!INCLUDE [auth-assign-group-roles](../includes/auth-assign-group-roles.md)] -1. In the Azure portal, use the search bar to navigate to the **App registrations** page. -1. On the **App registrations** page, select **+ New registration**. -1. On the **Register an application** page: - - For the **Name** field, enter a descriptive value that includes the app name and the target environment. - - For the **Supported account types**, select **Accounts in this organizational directory only (Microsoft Customer Led only - Single tenant)**, or whichever option best fits your requirements. -1. Select **Register** to register your app and create the service principal. - - :::image type="content" source="../../media/app-registration.png" alt-text="A screenshot showing how to create an app registration in the Azure portal."::: - -1. On the **App registration** page for your app, copy the **Application (client) ID** and **Directory (tenant) ID** and paste them in a temporary location for later use in your app code configurations. -1. Select **Add a certificate or secret** to set up credentials for your app. -1. On the **Certificates & secrets** page, select **+ New client secret**. -1. In the **Add a client secret** flyout panel that opens: - - For the **Description**, enter a value of Current. - - For the **Expires** value, leave the default recommended value of 180 days. - - Select **Add** to add the secret. -1. On the **Certificates & secrets** page, copy the **Value** property of the client secret for use in a future step. - - > [!NOTE] - > The client secret value is only displayed once after the app registration is created. You can add more client secrets without invalidating this client secret, but there's no way to display this value again. - -### [Azure CLI](#tab/azure-cli) - -Azure CLI commands can be run in the [Azure Cloud Shell](https://shell.azure.com) or on a workstation with the [Azure CLI installed](/cli/azure/install-azure-cli). - -1. Use the [az ad sp create-for-rbac](/cli/azure/ad/sp#az-ad-sp-create-for-rbac) command to create a new app registration and service principal for the app. - - ```azurecli - az ad sp create-for-rbac --name - ``` - - The output of this command resembles the following JSON: - - ```json - { - "appId": "00000000-0000-0000-0000-000000000000", - "displayName": "", - "password": "abcdefghijklmnopqrstuvwxyz", - "tenant": "11111111-1111-1111-1111-111111111111" - } - ``` - -1. Copy this output into a temporary file in a text editor, as you'll need these values in a future step. - - > [!NOTE] - > The client secret value is only displayed once after the app registration is created. You can add more client secrets without invalidating this client secret, but there's no way to display this value again. - ---- - -## Create a Microsoft Entra group for local development - -Create a Microsoft Entra group to encapsulate the roles (permissions) the app needs in local development rather than assigning the roles to individual service principal objects. This approach offers the following advantages: - -- Every developer has the same roles assigned at the group level. -- If a new role is needed for the app, it only needs to be added to the group for the app. -- If a new developer joins the team, a new application service principal is created for the developer and added to the group, ensuring the developer has the right permissions to work on the app. - -### [Azure portal](#tab/azure-portal) - -1. Navigate to the **Microsoft Entra ID** overview page in the Azure portal. -1. Select **All groups** from the left-hand menu. -1. On the **Groups** page, select **New group**. -1. On the **New group** page, fill out the following form fields: - - **Group type**: Select **Security**. - - **Group name**: Enter a name for the group that includes a reference to the app or environment name. - - **Group description**: Enter a description that explains the purpose of the group. - - :::image type="content" source="../../media/create-group.png" alt-text="A screenshot showing how to create a group in the Azure portal."::: - -1. Select the **No members selected** link under **Members** to add members to the group. -1. In the flyout panel that opens, search for the service principal you created earlier and select it from the filtered results. Choose the **Select** button at the bottom of the panel to confirm your selection. -1. Select **Create** at the bottom of the **New group** page to create the group and return to the **All groups** page. If you don't see the new group listed, wait a moment and refresh the page. - -### [Azure CLI](#tab/azure-cli) - -1. Use the [az ad group create](/cli/azure/ad/group#az-ad-group-create) command to create groups in Microsoft Entra ID. - - ```azurecli - az ad group create \ - --display-name \ - --mail-nickname \ - --description - ``` - - The `--display-name` and `--mail-nickname` parameters are required. The name given to the group should be based on the name and environment of the app to indicate the group's purpose. - -1. To add members to the group, you need the object ID of the application service principal, which is different than the application ID. Use the [az ad sp list](/cli/azure/ad/sp#az-ad-sp-list) command to list the available service principals: - - ```azurecli - az ad sp list \ - --filter "startswith(displayName, '')" \ - --query "[].{objectId:id, displayName:displayName}" - ``` - - The `--filter` parameter accepts OData-style filters and can be used to filter the list as shown. The `--query` parameter limits the output to only the columns of interest. - -1. Use the [az ad group member add](/cli/azure/ad/group/member#az-ad-group-member-add) command to add members to the group: - - ```azurecli - az ad group member add \ - --group \ - --member-id - ``` - ---- - -## Assign roles to the group - -Next, determine what roles (permissions) your app needs on what resources and assign those roles to the Microsoft Entra group you created. Groups can be assigned a role at the resource, resource group, or subscription scope. This example shows how to assign roles at the resource group scope, since most apps group all their Azure resources into a single resource group. - -### [Azure portal](#tab/azure-portal) - -1. In the Azure portal, navigate to the **Overview** page of the resource group that contains your app. -1. Select **Access control (IAM)** from the left navigation. -1. On the **Access control (IAM)** page, select **+ Add** and then choose **Add role assignment** from the drop-down menu. The **Add role assignment** page provides several tabs to configure and assign roles. -1. On the **Role** tab, use the search box to locate the role you want to assign. Select the role, and then choose **Next**. -1. On the **Members** tab: - - For the **Assign access to** value, select **User, group, or service principal** . - - For the **Members** value, choose **+ Select members** to open the **Select members** flyout panel. - - Search for the Microsoft Entra group you created earlier and select it from the filtered results. Choose **Select** to select the group and close the flyout panel. - - Select **Review + assign** at the bottom of the **Members** tab. - - :::image type="content" source="../../media/app-role-assignment.png" alt-text="A screenshot showing how to assign a role to the Microsoft Entra group."::: - -1. On the **Review + assign** tab, select **Review + assign** at the bottom of the page. - -### [Azure CLI](#tab/azure-cli) - -1. Use the [az role definition list](/cli/azure/role/definition#az-role-definition-list) command to get the names of the roles that a service principal can be assigned to: - - ```azurecli - az role definition list \ - --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \ - --output table - ``` - -1. Use the [az role assignment create](/cli/azure/role/assignment#az-role-assignment-create) command to assign a role to an application service principal: - - ```azurecli - az role assignment create \ - --assignee "" \ - --role "" \ - --resource-group "" - ``` - - For information on assigning permissions at the resource or subscription level using the Azure CLI, see [Assign Azure roles using the Azure CLI](/azure/role-based-access-control/role-assignments-cli). - ---- - -## Set the app environment variables - -At runtime, certain credentials from the [Azure Identity library](/dotnet/api/azure.identity?view=azure-dotnet&preserve-view=true), such as `DefaultAzureCredential`, `EnvironmentCredential`, and `ClientSecretCredential`, search for service principal information by convention in the environment variables. There are multiple ways to configure environment variables when working with .NET, depending on your tooling and environment. - -Regardless of the approach you choose, configure the following environment variables for a service principal: - -- `AZURE_CLIENT_ID`: Used to identify the registered app in Azure. -- `AZURE_TENANT_ID`: The ID of the Microsoft Entra tenant. -- `AZURE_CLIENT_SECRET`: The secret credential that was generated for the app. - -### [Visual Studio](#tab/visual-studio) - -In Visual Studio, environment variables can be set in the `launchsettings.json` file in the `Properties` folder of your project. These values are pulled in automatically when the app starts. However, these configurations don't travel with your app during deployment, so you need to set up environment variables on your target hosting environment. - -```json -"profiles": { - "SampleProject": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:7177;http://localhost:5177", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "AZURE_CLIENT_ID": "", - "AZURE_TENANT_ID":"", - "AZURE_CLIENT_SECRET": "" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "AZURE_CLIENT_ID": "", - "AZURE_TENANT_ID":"", - "AZURE_CLIENT_SECRET": "" - } - } - } -``` - -### [Visual Studio Code](#tab/vs-code) - -In Visual Studio Code, environment variables can be set in the `launch.json` file of your project. These values are pulled in automatically when the app starts. However, these configurations don't travel with your app during deployment, so you need to set up environment variables on your target hosting environment. - -```json -"configurations": [ -{ - "env": { - "ASPNETCORE_ENVIRONMENT": "Development", - "AZURE_CLIENT_ID": "", - "AZURE_TENANT_ID":"", - "AZURE_CLIENT_SECRET": "" - } -} -``` - -### [Windows](#tab/windows) - -You can set environment variables for Windows from the command line. However, the values are accessible to all apps running on that operating system and could cause conflicts, so use caution with this approach. Environment variables can be set at the user or system level. - -```bash -# Set user environment variables -setx ASPNETCORE_ENVIRONMENT "Development" -setx AZURE_CLIENT_ID "" -setx AZURE_TENANT_ID "" -setx AZURE_CLIENT_SECRET "" - -# Set system environment variables - requires running as admin -setx ASPNETCORE_ENVIRONMENT "Development" /m -setx AZURE_CLIENT_ID "" /m -setx AZURE_TENANT_ID "" /m -setx AZURE_CLIENT_SECRET "" /m -``` - -PowerShell can also be used to set environment variables at the user or machine level: - -```powershell -# Set user environment variables -[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "User") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "", "User") -[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "", "User") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "", "User") - -# Set system environment variables - requires running as admin -[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "Machine") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "", "Machine") -[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "", "Machine") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "", "Machine") -``` - ---- +[!INCLUDE [auth-set-environment-variables](../includes/auth-set-environment-variables.md)] [!INCLUDE [Implement Service Principal](<../includes/implement-service-principal.md>)] diff --git a/docs/azure/sdk/authentication/on-premises-apps.md b/docs/azure/sdk/authentication/on-premises-apps.md index 02e22222b114f..7e1edad6d826f 100644 --- a/docs/azure/sdk/authentication/on-premises-apps.md +++ b/docs/azure/sdk/authentication/on-premises-apps.md @@ -3,170 +3,66 @@ title: Authenticate to Azure resources from .NET apps hosted on-premises description: This article describes how to authenticate your application to Azure services when using the Azure SDK for .NET in on-premises hosted apps. ms.topic: how-to ms.custom: devx-track-dotnet, engagement-fy23 -ms.date: 08/02/2024 +ms.date: 03/13/2025 --- # Authenticate to Azure resources from .NET apps hosted on-premises -Apps hosted outside of Azure (for example, on-premises or at a third-party data center) should use an application service principal to authenticate to Azure when accessing Azure resources. Application service principal objects are created using the app registration process in Azure. When an application service principal is created, a client ID and client secret will be generated for your app. The client ID, client secret, and your tenant ID are then stored in environment variables so they can be used by the Azure Identity library to authenticate your app to Azure at runtime. +Apps hosted outside of Azure, such as on-premises or in a third-party data center, should use an application service principal through [Microsoft Entra ID](/entra/fundamentals/whatis) to authenticate to Azure services. In the sections ahead, you learn: -A different app registration should be created for each environment the app is hosted in. This allows environment specific resource permissions to be configured for each service principal and make sure an app deployed to one environment doesn't talk to Azure resources that are part of another environment. - -## 1 - Register the application in Azure - -An app can be registered with Azure using either the Azure portal or the Azure CLI. - -### [Azure portal](#tab/azure-portal) - -Sign in to the [Azure portal](https://portal.azure.com/) and follow these steps. - -| Instructions | Screenshot | -|:----------------|-----------:| -| [!INCLUDE [Create app registration step 1](<../includes/on-premises-app-registration-azure-portal-1.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-1-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-1.png" alt-text="A screenshot showing how to use the top search bar in the Azure portal to find and navigate to the App registrations page." ::: | -| [!INCLUDE [Create app registration step 2](<../includes/on-premises-app-registration-azure-portal-2.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-2-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-2.png" alt-text="A screenshot showing the location of the New registration button in the App registrations page." ::: | -| [!INCLUDE [Create app registration step 3](<../includes/on-premises-app-registration-azure-portal-3.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-3-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-3.png" alt-text="A screenshot showing how to fill out the Register an application page by giving the app a name and specifying supported account types as accounts in this organizational directory only." ::: | -| [!INCLUDE [Create app registration step 4](<../includes/on-premises-app-registration-azure-portal-4.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-4-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-4.png" alt-text="A screenshot of the App registration page after the app registration has been completed. This screenshot shows the location of the application ID and tenant ID which will be needed in a future step. It also shows the location of the link to use to add an application secret for the app." ::: | -| [!INCLUDE [Create app registration step 5](<../includes/on-premises-app-registration-azure-portal-5.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-5-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-5.png" alt-text="A screenshot showing the location of the link to use to create a new client secret on the certificates and secrets page." ::: | -| [!INCLUDE [Create app registration step 6](<../includes/on-premises-app-registration-azure-portal-6.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-6-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-6.png" alt-text="A screenshot showing the page where a new client secret is added for the application service principal created by the app registration process." ::: | -| [!INCLUDE [Create app registration step 7](<../includes/on-premises-app-registration-azure-portal-7.md>)] | :::image type="content" source="../media/on-premises-app-registration-azure-portal-7-240px.png" lightbox="../media/on-premises-app-registration-azure-portal-7.png" alt-text="A screenshot showing the page with the generated client secret." ::: | - -### [Azure CLI](#tab/azure-cli) - -```azurecli -az ad sp create-for-rbac --name -``` +- How to register an application with Microsoft Entra to create a service principal +- How to assign roles to scope permissions +- How to authenticate using a service principal from your app code -The output of the command will be similar to the following. Make note of these values or keep this window open as you will need these values in the next step and will not be able to view the password (client secret) value again. +Using dedicated application service principals allows you to adhere to the principle of least privilege when accessing Azure resources. Permissions are limited to the specific requirements of the app during development, preventing accidental access to Azure resources intended for other apps or services. This approach also helps avoid issues when the app is moved to production by ensuring it isn't over-privileged in the development environment. -```json -{ - "appId": "00000000-1111-2222-3333-444444444444", - "displayName": "msdocs-dotnet-sdk-auth-prod", - "password": "abcdefghijklmnopqrstuvwxyz", - "tenant": "00000000-0000-0000-0000-000000000000" -} -``` +A different app registration should be created for each environment the app is hosted in. This allows environment specific resource permissions to be configured for each service principal and make sure an app deployed to one environment doesn't talk to Azure resources that are part of another environment. ---- +[!INCLUDE [auth-create-app-registration](../includes/auth-create-app-registration.md)] -## 2 - Assign roles to the application service principal +## Assign roles to the application service principal -Next, you need to determine what roles (permissions) your app needs on what resources and assign those roles to your app. Roles can be assigned a role at a resource, resource group, or subscription scope. This example shows how to assign roles for the service principal at the resource group scope, since most apps group all their Azure resources into a single resource group. +Next, determine what roles (permissions) your app needs on what resources and assign those roles to the service principal you created. Roles can be assigned at the resource, resource group, or subscription scope. This example shows how to assign roles at the resource group scope, since most apps group all their Azure resources into a single resource group. ### [Azure portal](#tab/azure-portal) -| Instructions | Screenshot | -|:----------------|-----------:| -| [!INCLUDE [Assign service principal to role step 1](<../includes/assign-service-principal-to-role-azure-portal-1.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-1-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-1.png" alt-text="A screenshot showing how to use the top search box in the Azure portal to locate and navigate to the resource group you want to assign roles (permissions) to." ::: | -| [!INCLUDE [Assign service principal to role step 2](<../includes/assign-service-principal-to-role-azure-portal-2.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-2-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-2.png" alt-text="A screenshot of the resource group page showing the location of the Access control (IAM) menu item." ::: | -| [!INCLUDE [Assign service principal to role step 3](<../includes/assign-service-principal-to-role-azure-portal-3.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-3-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-3.png" alt-text="A screenshot showing how to navigate to the role assignments tab and the location of the button used to add role assignments to a resource group." ::: | -| [!INCLUDE [Assign service principal to role step 4](<../includes/assign-service-principal-to-role-azure-portal-4.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-4-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-4.png" alt-text="A screenshot showing how to filter and select role assignments to be added to the resource group." ::: | -| [!INCLUDE [Assign service principal to role step 5](<../includes/assign-service-principal-to-role-azure-portal-5.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-5-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-5.png" alt-text="A screenshot showing the radio button to select to assign a role to a Microsoft Entra group and the link used to select the group to assign the role to." ::: | -| [!INCLUDE [Assign service principal to role step 6](<../includes/assign-service-principal-to-role-azure-portal-6.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-6-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-6.png" alt-text="A screenshot showing how to filter for and select the Microsoft Entra group for the application in the Select members dialog box." ::: | -| [!INCLUDE [Assign service principal to role step 7](<../includes/assign-service-principal-to-role-azure-portal-7.md>)] | :::image type="content" source="../media/assign-service-principal-to-role-azure-portal-7-240px.png" lightbox="../media/assign-service-principal-to-role-azure-portal-7.png" alt-text="A screenshot showing the completed Add role assignment page and the location of the Review + assign button used to complete the process." ::: | - -### [Azure CLI](#tab/azure-cli) - -A service principal is assigned a role in Azure using the [az role assignment create](/cli/azure/role/assignment#az-role-assignment-create) command: - -```azurecli -az role assignment create --assignee "{appId}" \ - --role "{roleName}" \ - --resource-group "{resourceGroupName}" -``` +1. In the Azure portal, navigate to the **Overview** page of the resource group that contains your app. +1. Select **Access control (IAM)** from the left navigation. +1. On the **Access control (IAM)** page, select **+ Add** and then choose **Add role assignment** from the drop-down menu. The **Add role assignment** page provides several tabs to configure and assign roles. +1. On the **Role** tab, use the search box to locate the role you want to assign. Select the role, and then choose **Next**. +1. On the **Members** tab: + - For the **Assign access to** value, select **User, group, or service principal** . + - For the **Members** value, choose **+ Select members** to open the **Select members** flyout panel. + - Search for the service principal you created earlier and select it from the filtered results. Choose **Select** to select the group and close the flyout panel. + - Select **Review + assign** at the bottom of the **Members** tab. -To get the role names to which a service principal can be assigned, use the [az role definition list](/cli/azure/role/definition#az-role-definition-list) command: + :::image type="content" source="../../media/app-role-assignment.png" alt-text="A screenshot showing how to assign a role to the service principal."::: -```azurecli -az role definition list \ - --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \ - --output table -``` +1. On the **Review + assign** tab, select **Review + assign** at the bottom of the page. -For example, to allow the service principal with the `appId` of `00000000-0000-0000-0000-000000000000` read, write, and delete access to Azure Storage blob containers and data to all storage accounts in the *msdocs-dotnet-sdk-auth-example* resource group, assign the application service principal to the *Storage Blob Data Contributor* role using the following command: - -```azurecli -az role assignment create --assignee "00000000-0000-0000-0000-000000000000" \ - --role "Storage Blob Data Contributor" \ - --resource-group "msdocs-dotnet-sdk-auth-example" -``` - -For information on assigning permissions at the resource or subscription level using the Azure CLI, see [Assign Azure roles using the Azure CLI](/azure/role-based-access-control/role-assignments-cli). - ---- - -## 3 - Configure environment variables for application - -The `DefaultAzureCredential` object will look for service principal credentials in a set of environment variables at runtime. There are multiple ways to configure environment variables when working with .NET depending on your tooling and environment. - -Regardless of which approach you choose, configure the following environment variables when working with a service principal: - -- `AZURE_CLIENT_ID` → The app ID value. -- `AZURE_TENANT_ID` → The tenant ID value. -- `AZURE_CLIENT_SECRET` → The password/credential generated for the app. - -### [IIS](#tab/iis-app-pool) - -If your app is hosted in IIS, it's recommended that you set environment variables per app pool to isolate settings between apps. - -```bash -appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='ASPNETCORE_ENVIRONMENT',value='Production']" /commit:apphost -appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='AZURE_CLIENT_ID',value='00000000-0000-0000-0000-000000000000']" /commit:apphost -appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='AZURE_TENANT_ID',value='11111111-1111-1111-1111-111111111111']" /commit:apphost -appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='Contoso'].environmentVariables.[name='AZURE_CLIENT_SECRET',value='=abcdefghijklmnopqrstuvwxyz']" /commit:apphost -``` - -You can also configure these settings directly using the `applicationPools` element inside of the `applicationHost.config` file: - -```xml - - - - - - - - - - -``` - -### [Windows](#tab/windows) - -You can set environment variables for Windows from the command line. However, when using this approach the values are accessible to all apps running on that operating system and may cause conflicts if you aren't careful. Environment variables can be set at either the user or system level. +### [Azure CLI](#tab/azure-cli) -```bash -# Set user environment variables -setx ASPNETCORE_ENVIRONMENT "Development" -setx AZURE_CLIENT_ID "00000000-0000-0000-0000-000000000000" -setx AZURE_TENANT_ID "11111111-1111-1111-1111-111111111111" -setx AZURE_CLIENT_SECRET "=abcdefghijklmnopqrstuvwxyz" +1. Use the [az role definition list](/cli/azure/role/definition#az-role-definition-list) command to get the names of the roles that a service principal can be assigned to: -# Set system environment variables - requires running as admin -setx ASPNETCORE_ENVIRONMENT "Development" -setx AZURE_CLIENT_ID "00000000-0000-0000-0000-000000000000" /m -setx AZURE_TENANT_ID "11111111-1111-1111-1111-111111111111" /m -setx AZURE_CLIENT_SECRET "=abcdefghijklmnopqrstuvwxyz" /m -``` + ```azurecli + az role definition list \ + --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \ + --output table + ``` -You can also use PowerShell to set environment variables at either the user or machine level: +1. Use the [az role assignment create](/cli/azure/role/assignment#az-role-assignment-create) command to assign a role to an application service principal: -```powershell -# Set user environment variables -[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "User") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "00000000-0000-0000-0000-000000000000", "User") -[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "11111111-1111-1111-1111-111111111111", "User") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "=abcdefghijklmnopqrstuvwxyz", "User") + ```azurecli + az role assignment create \ + --assignee "" \ + --role "" \ + --resource-group "" + ``` -# Set system environment variables - requires running as admin -[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "Machine") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "00000000-0000-0000-0000-000000000000", "Machine") -[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "11111111-1111-1111-1111-111111111111", "Machine") -[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "=abcdefghijklmnopqrstuvwxyz", "Machine") -``` + For information on assigning permissions at the resource or subscription level using the Azure CLI, see [Assign Azure roles using the Azure CLI](/azure/role-based-access-control/role-assignments-cli). --- -## 4 - Implement DefaultAzureCredential in your application +[!INCLUDE [auth-set-environment-variables](../includes/auth-set-environment-variables.md)] -[!INCLUDE [Implement DefaultAzureCredential](<../includes/implement-defaultazurecredential.md>)] +[!INCLUDE [implement-service-principal](../includes/implement-service-principal.md)] diff --git a/docs/azure/sdk/includes/auth-assign-group-roles.md b/docs/azure/sdk/includes/auth-assign-group-roles.md new file mode 100644 index 0000000000000..2a524c9cefa9d --- /dev/null +++ b/docs/azure/sdk/includes/auth-assign-group-roles.md @@ -0,0 +1,47 @@ +--- +ms.topic: include +ms.date: 03/13/2025 +--- + +## Assign roles to the group + +Next, determine what roles (permissions) your app needs on what resources and assign those roles to the Microsoft Entra group you created. Groups can be assigned a role at the resource, resource group, or subscription scope. This example shows how to assign roles at the resource group scope, since most apps group all their Azure resources into a single resource group. + +### [Azure portal](#tab/azure-portal) + +1. In the Azure portal, navigate to the **Overview** page of the resource group that contains your app. +1. Select **Access control (IAM)** from the left navigation. +1. On the **Access control (IAM)** page, select **+ Add** and then choose **Add role assignment** from the drop-down menu. The **Add role assignment** page provides several tabs to configure and assign roles. +1. On the **Role** tab, use the search box to locate the role you want to assign. Select the role, and then choose **Next**. +1. On the **Members** tab: + - For the **Assign access to** value, select **User, group, or service principal** . + - For the **Members** value, choose **+ Select members** to open the **Select members** flyout panel. + - Search for the Microsoft Entra group you created earlier and select it from the filtered results. Choose **Select** to select the group and close the flyout panel. + - Select **Review + assign** at the bottom of the **Members** tab. + + :::image type="content" source="../../media/group-role-assignment.png" alt-text="A screenshot showing how to assign a role to the Microsoft Entra group."::: + +1. On the **Review + assign** tab, select **Review + assign** at the bottom of the page. + +### [Azure CLI](#tab/azure-cli) + +1. Use the [az role definition list](/cli/azure/role/definition#az-role-definition-list) command to get the names of the roles that a Microsoft Entra group or service principal can be assigned to: + + ```azurecli + az role definition list \ + --query "sort_by([].{roleName:roleName, description:description}, &roleName)" \ + --output table + ``` + +1. Use the [az role assignment create](/cli/azure/role/assignment#az-role-assignment-create) command to assign a role to the Microsoft Entra group you created: + + ```azurecli + az role assignment create \ + --assignee "" \ + --role "" \ + --resource-group "" + ``` + + For information on assigning permissions at the resource or subscription level using the Azure CLI, see [Assign Azure roles using the Azure CLI](/azure/role-based-access-control/role-assignments-cli). + +--- diff --git a/docs/azure/sdk/includes/auth-create-app-registration.md b/docs/azure/sdk/includes/auth-create-app-registration.md new file mode 100644 index 0000000000000..499df0dae5111 --- /dev/null +++ b/docs/azure/sdk/includes/auth-create-app-registration.md @@ -0,0 +1,59 @@ +--- +ms.topic: include +ms.date: 03/13/2025 +--- + +## Register the app in Azure + +Application service principal objects are created through an app registration in Azure using either the Azure portal or Azure CLI. + +### [Azure portal](#tab/azure-portal) + +1. In the Azure portal, use the search bar to navigate to the **App registrations** page. +1. On the **App registrations** page, select **+ New registration**. +1. On the **Register an application** page: + - For the **Name** field, enter a descriptive value that includes the app name and the target environment. + - For the **Supported account types**, select **Accounts in this organizational directory only (Microsoft Customer Led only - Single tenant)**, or whichever option best fits your requirements. +1. Select **Register** to register your app and create the service principal. + + :::image type="content" source="../../media/app-registration.png" alt-text="A screenshot showing how to create an app registration in the Azure portal."::: + +1. On the **App registration** page for your app, copy the **Application (client) ID** and **Directory (tenant) ID** and paste them in a temporary location for later use in your app code configurations. +1. Select **Add a certificate or secret** to set up credentials for your app. +1. On the **Certificates & secrets** page, select **+ New client secret**. +1. In the **Add a client secret** flyout panel that opens: + - For the **Description**, enter a value of Current. + - For the **Expires** value, leave the default recommended value of 180 days. + - Select **Add** to add the secret. +1. On the **Certificates & secrets** page, copy the **Value** property of the client secret for use in a future step. + + > [!NOTE] + > The client secret value is only displayed once after the app registration is created. You can add more client secrets without invalidating this client secret, but there's no way to display this value again. + +### [Azure CLI](#tab/azure-cli) + +Azure CLI commands can be run in the [Azure Cloud Shell](https://shell.azure.com) or on a workstation with the [Azure CLI installed](/cli/azure/install-azure-cli). + +1. Use the [az ad sp create-for-rbac](/cli/azure/ad/sp#az-ad-sp-create-for-rbac) command to create a new app registration and service principal for the app. + + ```azurecli + az ad sp create-for-rbac --name + ``` + + The output of this command resembles the following JSON: + + ```json + { + "appId": "00000000-0000-0000-0000-000000000000", + "displayName": "", + "password": "abcdefghijklmnopqrstuvwxyz", + "tenant": "11111111-1111-1111-1111-111111111111" + } + ``` + +1. Copy this output into a temporary file in a text editor, as you'll need these values in a future step. + + > [!NOTE] + > The client secret value is only displayed once after the app registration is created. You can add more client secrets without invalidating this client secret, but there's no way to display this value again. + +--- diff --git a/docs/azure/sdk/includes/auth-create-entra-group.md b/docs/azure/sdk/includes/auth-create-entra-group.md new file mode 100644 index 0000000000000..e838e01048ac6 --- /dev/null +++ b/docs/azure/sdk/includes/auth-create-entra-group.md @@ -0,0 +1,61 @@ +--- +ms.topic: include +ms.date: 03/11/2025 +--- + +## Create a Microsoft Entra group for local development + +Create a Microsoft Entra group to encapsulate the roles (permissions) the app needs in local development rather than assigning the roles to individual service principal objects. This approach offers the following advantages: + +- Every developer has the same roles assigned at the group level. +- If a new role is needed for the app, it only needs to be added to the group for the app. +- If a new developer joins the team, a new application service principal is created for the developer and added to the group, ensuring the developer has the right permissions to work on the app. + +### [Azure portal](#tab/azure-portal) + +1. Navigate to the **Microsoft Entra ID** overview page in the Azure portal. +1. Select **All groups** from the left-hand menu. +1. On the **Groups** page, select **New group**. +1. On the **New group** page, fill out the following form fields: + - **Group type**: Select **Security**. + - **Group name**: Enter a name for the group that includes a reference to the app or environment name. + - **Group description**: Enter a description that explains the purpose of the group. + + :::image type="content" source="../../media/create-group.png" alt-text="A screenshot showing how to create a group in the Azure portal."::: + +1. Select the **No members selected** link under **Members** to add members to the group. +1. In the flyout panel that opens, search for the service principal you created earlier and select it from the filtered results. Choose the **Select** button at the bottom of the panel to confirm your selection. +1. Select **Create** at the bottom of the **New group** page to create the group and return to the **All groups** page. If you don't see the new group listed, wait a moment and refresh the page. + +### [Azure CLI](#tab/azure-cli) + +1. Use the [az ad group create](/cli/azure/ad/group#az-ad-group-create) command to create groups in Microsoft Entra ID. + + ```azurecli + az ad group create \ + --display-name \ + --mail-nickname \ + --description + ``` + + The `--display-name` and `--mail-nickname` parameters are required. The name given to the group should be based on the name and environment of the app to indicate the group's purpose. + +1. To add members to the group, you need the object ID of the application service principal, which is different than the application ID. Use the [az ad sp list](/cli/azure/ad/sp#az-ad-sp-list) command to list the available service principals: + + ```azurecli + az ad sp list \ + --filter "startswith(displayName, '')" \ + --query "[].{objectId:id, displayName:displayName}" + ``` + + The `--filter` parameter accepts OData-style filters and can be used to filter the list as shown. The `--query` parameter limits the output to only the columns of interest. + +1. Use the [az ad group member add](/cli/azure/ad/group/member#az-ad-group-member-add) command to add members to the group: + + ```azurecli + az ad group member add \ + --group \ + --member-id + ``` + +--- diff --git a/docs/azure/sdk/includes/auth-set-environment-variables.md b/docs/azure/sdk/includes/auth-set-environment-variables.md new file mode 100644 index 0000000000000..7fece8908516e --- /dev/null +++ b/docs/azure/sdk/includes/auth-set-environment-variables.md @@ -0,0 +1,97 @@ +--- +ms.topic: include +ms.date: 03/11/2025 +--- + +## Set the app environment variables + +At runtime, certain credentials from the [Azure Identity library](/dotnet/api/azure.identity?view=azure-dotnet&preserve-view=true), such as `DefaultAzureCredential`, `EnvironmentCredential`, and `ClientSecretCredential`, search for service principal information by convention in the environment variables. There are multiple ways to configure environment variables when working with .NET, depending on your tooling and environment. + +Regardless of the approach you choose, configure the following environment variables for a service principal: + +- `AZURE_CLIENT_ID`: Used to identify the registered app in Azure. +- `AZURE_TENANT_ID`: The ID of the Microsoft Entra tenant. +- `AZURE_CLIENT_SECRET`: The secret credential that was generated for the app. + +### [Visual Studio](#tab/visual-studio) + +In Visual Studio, environment variables can be set in the `launchsettings.json` file in the `Properties` folder of your project. These values are pulled in automatically when the app starts. However, these configurations don't travel with your app during deployment, so you need to set up environment variables on your target hosting environment. + +```json +"profiles": { + "SampleProject": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7177;http://localhost:5177", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "AZURE_CLIENT_ID": "", + "AZURE_TENANT_ID":"", + "AZURE_CLIENT_SECRET": "" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "AZURE_CLIENT_ID": "", + "AZURE_TENANT_ID":"", + "AZURE_CLIENT_SECRET": "" + } + } + } +``` + +### [Visual Studio Code](#tab/vs-code) + +In Visual Studio Code, environment variables can be set in the `launch.json` file of your project. These values are pulled in automatically when the app starts. However, these configurations don't travel with your app during deployment, so you need to set up environment variables on your target hosting environment. + +```json +"configurations": [ +{ + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "AZURE_CLIENT_ID": "", + "AZURE_TENANT_ID":"", + "AZURE_CLIENT_SECRET": "" + } +} +``` + +### [Windows](#tab/windows) + +You can set environment variables for Windows from the command line. However, the values are accessible to all apps running on that operating system and could cause conflicts, so use caution with this approach. Environment variables can be set at the user or system level. + +```bash +# Set user environment variables +setx ASPNETCORE_ENVIRONMENT "Development" +setx AZURE_CLIENT_ID "" +setx AZURE_TENANT_ID "" +setx AZURE_CLIENT_SECRET "" + +# Set system environment variables - requires running as admin +setx ASPNETCORE_ENVIRONMENT "Development" /m +setx AZURE_CLIENT_ID "" /m +setx AZURE_TENANT_ID "" /m +setx AZURE_CLIENT_SECRET "" /m +``` + +PowerShell can also be used to set environment variables at the user or system level: + +```powershell +# Set user environment variables +[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "User") +[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "", "User") +[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "", "User") +[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "", "User") + +# Set system environment variables - requires running as admin +[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "Machine") +[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "", "Machine") +[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "", "Machine") +[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "", "Machine") +``` + +--- diff --git a/docs/core/compatibility/10.0.md b/docs/core/compatibility/10.0.md index 6ed38abe7cfa5..0dc71112bc153 100644 --- a/docs/core/compatibility/10.0.md +++ b/docs/core/compatibility/10.0.md @@ -41,10 +41,11 @@ If you're migrating an app to .NET 10, the breaking changes listed here might af ## Cryptography -| Title | Type of change | Introduced version | -|------------------------------------------------------------------------------------------------------------|-------------------|--------------------| -| [X500DistinguishedName validation is stricter](cryptography/10.0/x500distinguishedname-validation.md) | Behavioral change | Preview 1 | -| [Environment variable renamed to DOTNET_OPENSSL_VERSION_OVERRIDE](cryptography/10.0/version-override.md) | Behavioral change | Preview 1 | +| Title | Type of change | Introduced version | +|----------------------------------------------------------------------------------------------------------|---------------------------------------|--------------------| +| [X500DistinguishedName validation is stricter](cryptography/10.0/x500distinguishedname-validation.md) | Behavioral change | Preview 1 | +| [X509Certificate and PublicKey key parameters can be null](cryptography/10.0/x509-publickey-null.md) | Behavioral/source incompatible change | Preview 3 | +| [Environment variable renamed to DOTNET_OPENSSL_VERSION_OVERRIDE](cryptography/10.0/version-override.md) | Behavioral change | Preview 1 | ## SDK diff --git a/docs/core/compatibility/cryptography/10.0/x509-publickey-null.md b/docs/core/compatibility/cryptography/10.0/x509-publickey-null.md new file mode 100644 index 0000000000000..6dec01cbf55f6 --- /dev/null +++ b/docs/core/compatibility/cryptography/10.0/x509-publickey-null.md @@ -0,0 +1,60 @@ +--- +title: "Breaking change - X509Certificate and PublicKey key parameters can be null" +description: "Learn about the breaking change in .NET 10 Preview 3 where key parameters in X509Certificate and PublicKey can be null." +ms.date: 3/13/2025 +ai-usage: ai-assisted +ms.custom: https://github.com/dotnet/docs/issues/45325 +--- + +# X509Certificate and PublicKey key parameters can be null + +In .NET 10, the behavior of and has changed. When these objects contain a key without algorithm parameters, they now return `null` instead of an empty array. + +## Version introduced + +.NET 10 Preview 3 + +## Previous behavior + + or objects that contained a key without algorithm parameters would return an empty array when accessing the key algorithm parameters. + +```csharp +byte[] parameters = certificate.GetKeyAlgorithmParameters(); +// parameters would be an empty array if no algorithm parameters were present +``` + +## New behavior + + or objects that contain a key without algorithm parameters will return `null` when accessing the key algorithm parameters. + +```csharp +byte[] parameters = certificate.GetKeyAlgorithmParameters(); +// parameters will be null if no algorithm parameters are present +``` + +## Type of breaking change + +This is both a [behavioral](../../categories.md#behavioral-change) and [source compatibility](../../categories.md#source-compatibility) change. + +## Reason for change + +The , , and classes expose information about the *Subject Public Key Info*. One of the properties of the *Subject Public Key Info* is the parameters for the algorithm. A *Subject Public Key Info* is not required to contain algorithm parameters. Previously, this was represented as an empty byte array, which is not valid ASN.1. Attempting to encode or decode it would result in an exception. To more clearly represent absent key parameters, `null` is now returned, and the members that return algorithm parameters have been annotated to return nullable values. + +## Recommended action + +When accessing a member that returns information about a subject public key info's algorithm parameters, expect the member to possibly return `null` and handle the `null` value accordingly. + +```csharp +byte[] parameters = certificate.GetKeyAlgorithmParameters(); +if (parameters == null) +{ + // Handle the absence of algorithm parameters +} +``` + +## Affected APIs + +- +- +- +- diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index 11e4aa6ca3f16..31b1b705cb6bc 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -32,6 +32,8 @@ items: items: - name: X500DistinguishedName validation is stricter href: cryptography/10.0/x500distinguishedname-validation.md + - name: X509Certificate and PublicKey key parameters can be null + href: cryptography/10.0/x509-publickey-null.md - name: Environment variable renamed to DOTNET_OPENSSL_VERSION_OVERRIDE href: cryptography/10.0/version-override.md - name: Globalization @@ -1618,6 +1620,8 @@ items: items: - name: X500DistinguishedName validation is stricter href: cryptography/10.0/x500distinguishedname-validation.md + - name: X509Certificate and PublicKey key parameters can be null + href: cryptography/10.0/x509-publickey-null.md - name: Environment variable renamed to DOTNET_OPENSSL_VERSION_OVERRIDE href: cryptography/10.0/version-override.md - name: .NET 9 diff --git a/docs/core/extensions/dependency-injection-guidelines.md b/docs/core/extensions/dependency-injection-guidelines.md index a270b0adcf7f5..aea1236f9cc8c 100644 --- a/docs/core/extensions/dependency-injection-guidelines.md +++ b/docs/core/extensions/dependency-injection-guidelines.md @@ -185,7 +185,7 @@ In the preceding code, the `implementationFactory` is given a lambda expression :::image type="content" source="media/deadlock-with-async-factory-01.png" lightbox="media/deadlock-with-async-factory-01.png" alt-text="Anti-pattern: Deadlock with async factory inner issue. Do not copy!"::: -For more information on asynchronous guidance, see [Asynchronous programming: Important info and advice](../../csharp/asynchronous-programming/async-scenarios.md#important-info-and-advice). For more information debugging deadlocks, see [Debug a deadlock in .NET](../diagnostics/debug-deadlock.md). +For more information on asynchronous guidance, see [Asynchronous programming: Important info and advice](../../csharp/asynchronous-programming/async-scenarios.md#review-considerations-for-asynchronous-programming). For more information debugging deadlocks, see [Debug a deadlock in .NET](../diagnostics/debug-deadlock.md). When you're running this anti-pattern and the deadlock occurs, you can view the two threads waiting from Visual Studio's Parallel Stacks window. For more information, see [View threads and tasks in the Parallel Stacks window](/visualstudio/debugger/using-the-parallel-stacks-window). diff --git a/docs/core/whats-new/dotnet-9/overview.md b/docs/core/whats-new/dotnet-9/overview.md index b054069c19e35..47601bfa0af56 100644 --- a/docs/core/whats-new/dotnet-9/overview.md +++ b/docs/core/whats-new/dotnet-9/overview.md @@ -45,7 +45,6 @@ For more information, see [What's new in the .NET 9 libraries](libraries.md). The .NET 9 SDK introduces _workload sets_, where all of your workloads stay at a single, specific version until explicitly updated. For tools, a new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets users (instead of tool authors) decide whether a tool is allowed to run on a newer .NET runtime version than the version the tool targets. In addition: - Unit testing has better MSBuild integration that allows you to run tests in parallel. -- NuGet security audits run on both direct and transitive package references, by default. - The terminal logger is enabled by default and also has improved usability. For example, the total count of failures and warnings is now summarized at the end of a build. - New MSBuild script analyzers ("build checks") are available. - The SDK can detect and adjust for version mismatches between the .NET SDK and MSBuild. diff --git a/docs/core/whats-new/dotnet-9/sdk.md b/docs/core/whats-new/dotnet-9/sdk.md index d5966c40e2f23..8c39da746f504 100644 --- a/docs/core/whats-new/dotnet-9/sdk.md +++ b/docs/core/whats-new/dotnet-9/sdk.md @@ -111,10 +111,6 @@ The message lines of the warning no longer have the repeated project and locatio If you have feedback about the terminal logger, you can provide it in the [MSBuild repository](https://github.com/dotnet/msbuild/issues). -## NuGet security audits - -Starting in .NET 8, `dotnet restore` [audits NuGet package references for known vulnerabilities](../../tools/dotnet-restore.md#audit-for-security-vulnerabilities). In .NET 9, the default mode has changed from auditing only _direct_ package references to auditing both _direct_ and _transitive_ package references. - ## Faster NuGet dependency resolution for large repos The NuGet dependency resolver has been overhauled to improve performance and scalability for all `` projects. Enabled by default, the new algorithm speeds up restore operations without compromising on functionality, strictly adhering to the core dependency resolution rules. diff --git a/docs/csharp/asynchronous-programming/async-scenarios.md b/docs/csharp/asynchronous-programming/async-scenarios.md index 9f471bff3c84f..7246ade88a18b 100644 --- a/docs/csharp/asynchronous-programming/async-scenarios.md +++ b/docs/csharp/asynchronous-programming/async-scenarios.md @@ -1,97 +1,93 @@ --- title: Asynchronous programming scenarios -description: Learn about the C# language-level asynchronous programming model provided by .NET Core. +description: Learn about the C# language-level asynchronous programming model provided by .NET Core and explore example code for I/O-bound and CPU-bound scenarios. author: BillWagner -ms.date: 02/08/2023 +ms.date: 03/12/2025 ms.subservice: async-task-programming --- # Asynchronous programming scenarios -If you have any I/O-bound needs (such as requesting data from a network, accessing a database, or reading and writing to a file system), you'll want to utilize asynchronous programming. You could also have CPU-bound code, such as performing an expensive calculation, which is also a good scenario for writing async code. +If your code implements I/O-bound scenarios to support network data requests, database access, or file system read/writes, asynchronous programming is the best approach. You can also write asynchronous code for CPU-bound scenarios like expensive calculations. -C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. It follows what is known as the [Task-based Asynchronous Pattern (TAP)](../../standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md). +C# has a language-level asynchronous programming model that allows you to easily write asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. The model follows what is known as the [Task-based asynchronous pattern (TAP)](../../standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md). -## Overview of the asynchronous model +## Explore the asynchronous programming model -The core of async programming is the `Task` and `Task` objects, which model asynchronous operations. They are supported by the `async` and `await` keywords. The model is fairly simple in most cases: +The `Task` and `Task` objects represent the core of asynchronous programming. These objects are used to model asynchronous operations by supporting the `async` and `await` keywords. In most cases, the model is fairly simple for both I/O-bound and CPU-bound scenarios. Inside an `async` method: -- For I/O-bound code, you await an operation that returns a `Task` or `Task` inside of an `async` method. -- For CPU-bound code, you await an operation that is started on a background thread with the method. +- **I/O-bound code** starts an operation represented by a `Task` or `Task` object within the `async` method. +- **CPU-bound code** starts an operation on a background thread with the method. -The `await` keyword is where the magic happens. It yields control to the caller of the method that performed `await`, and it ultimately allows a UI to be responsive or a service to be elastic. While [there are ways](../../standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md) to approach async code other than `async` and `await`, this article focuses on the language-level constructs. +In both cases, an active `Task` represents an asynchronous operation that might not be complete. + +The `await` keyword is where the magic happens. It yields control to the caller of the method that contains the `await` expression, and ultimately allows the UI to be responsive or a service to be elastic. While [there are ways](../../standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md) to approach asynchronous code other than by using the `async` and `await` expressions, this article focuses on the language-level constructs. > [!NOTE] -> In some of following examples class is used to download some data from a web service. -> The `s_httpClient` object used in these examples is a static field of `Program` class (please check the complete example): +> Some examples presented in this article use the class to download data from a web service. In the example code, the `s_httpClient` object is a static field of type `Program` class: > > `private static readonly HttpClient s_httpClient = new();` +> +> For more information, see the [complete example code](#review-the-complete-example) at the end of this article. -### I/O-bound example: Download data from a web service - -You may need to download some data from a web service when a button is pressed but don't want to block the UI thread. It can be accomplished like this: - -:::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="UnblockingDownload"::: - -The code expresses the intent (downloading data asynchronously) without getting bogged down in interacting with `Task` objects. - -### CPU-bound example: Perform a calculation for a game - -Say you're writing a mobile game where pressing a button can inflict damage on many enemies on the screen. Performing the damage calculation can be expensive, and doing it on the UI thread would make the game appear to pause as the calculation is performed! +### Review underlying concepts -The best way to handle this is to start a background thread, which does the work using `Task.Run`, and await its result using `await`. This allows the UI to feel smooth as the work is being done. +When you implement asynchronous programming in your C# code, the compiler transforms your program into a state machine. This construct tracks various operations and state in your code, such as yielding execution when the code reaches an `await` expression, and resuming execution when a background job completes. -:::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="PerformGameCalculation"::: +In terms of computer science theory, asynchronous programming is an implementation of the [Promise model of asynchrony](https://en.wikipedia.org/wiki/Futures_and_promises). -This code clearly expresses the intent of the button's click event, it doesn't require managing a background thread manually, and it does so in a non-blocking way. +In the asynchronous programming model, there are several key concepts to understand: -### What happens under the covers +* You can use asynchronous code for both I/O-bound and CPU-bound code, but the implementation is different. +* Asynchronous code uses `Task` and `Task` objects as constructs to model work running in the background. +* The `async` keyword declares a method as an asynchronous method, which allows you to use the `await` keyword in the method body. +* When you apply the `await` keyword, the code suspends the calling method and yields control back to its caller until the task completes. +* You can only use the `await` expression in an asynchronous method. -On the C# side of things, the compiler transforms your code into a state machine that keeps track of things like yielding execution when an `await` is reached and resuming execution when a background job has finished. +### I/O-bound example: Download data from web service -For the theoretically inclined, this is an implementation of the [Promise Model of asynchrony](https://en.wikipedia.org/wiki/Futures_and_promises). +In this example, when the user selects a button, the app downloads data from a web service. You don't want to block the UI thread for the app during the download process. The following code accomplishes this task: -## Key pieces to understand +:::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="UnblockingDownload"::: -* Async code can be used for both I/O-bound and CPU-bound code, but differently for each scenario. -* Async code uses `Task` and `Task`, which are constructs used to model work being done in the background. -* The `async` keyword turns a method into an async method, which allows you to use the `await` keyword in its body. -* When the `await` keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. -* `await` can only be used inside an async method. +The code expresses the intent (downloading data asynchronously) without getting bogged down in interacting with `Task` objects. -## Recognize CPU-bound and I/O-bound work +### CPU-bound example: Run game calculation -The first two examples of this guide showed how you could use `async` and `await` for I/O-bound and CPU-bound work. It's key that you can identify when a job you need to do is I/O-bound or CPU-bound because it can greatly affect the performance of your code and could potentially lead to misusing certain constructs. +In the next example, a mobile game inflicts damage on several agents on the screen in response to a button event. Performing the damage calculation can be expensive. Running the calculation on the UI thread can cause display and UI interaction issues during the calculation. -Here are two questions you should ask before you write any code: +The best way to handle the task is to start a background thread to complete the work with the `Task.Run` method. The operation yields by using an `await` expression. The operation resumes when the task completes. This approach allows the UI to run smoothly while the work completes in the background. -1. Will your code be "waiting" for something, such as data from a database? +:::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="PerformGameCalculation"::: - If your answer is "yes", then your work is **I/O-bound**. +The code clearly expresses the intent of the button `Clicked` event. It doesn't require managing a background thread manually, and it completes the task in a nonblocking manner. -1. Will your code be performing an expensive computation? +## Recognize CPU-bound and I/O-bound scenarios - If you answered "yes", then your work is **CPU-bound**. +The previous examples demonstrate how to use the `async` modifier and `await` expression for I/O-bound and CPU-bound work. An example for each scenario showcases how the code is different based on where the operation is bound. To prepare for your implementation, you need to understand how to identify when an operation is I/O-bound or CPU-bound. Your implementation choice can greatly affect the performance of your code and potentially lead to misusing constructs. -If the work you have is **I/O-bound**, use `async` and `await` *without* `Task.Run`. You *should not* use the Task Parallel Library. +There are two primary questions to address before you write any code: -If the work you have is **CPU-bound** and you care about responsiveness, use `async` and `await`, but spawn off the work on another thread *with* `Task.Run`. If the work is appropriate for concurrency and parallelism, also consider using the [Task Parallel Library](../../standard/parallel-programming/task-parallel-library-tpl.md). +| Question | Scenario | Implementation | +| --- | --- | --- | +| _Should the code wait for a result or action, such as data from a database?_ | **I/O-bound** | Use the `async` modifier and `await` expression _without_ the `Task.Run` method.

Avoid using the Task Parallel Library. | +| _Should the code run an expensive computation?_ | **CPU-bound** | Use the `async` modifier and `await` expression, but spawn off the work on another thread with the `Task.Run` method. This approach addresses concerns with CPU responsiveness.

If the work is appropriate for concurrency and parallelism, also consider using the [Task Parallel Library](../../standard/parallel-programming/task-parallel-library-tpl.md). | -Additionally, you should always measure the execution of your code. For example, you may find yourself in a situation where your CPU-bound work is not costly enough compared with the overhead of context switches when multithreading. Every choice has its tradeoff, and you should pick the correct tradeoff for your situation. +Always measure the execution of your code. You might discover that your CPU-bound work isn't costly enough compared with the overhead of context switches when multithreading. Every choice has tradeoffs. Pick the correct tradeoff for your situation. -## More examples +## Explore other examples -The following examples demonstrate various ways you can write async code in C#. They cover a few different scenarios you may come across. +The examples in this section demonstrate several ways you can write asynchronous code in C#. They cover a few scenarios you might encounter. ### Extract data from a network -This snippet downloads the HTML from the given URL and counts the number of times the string ".NET" occurs in the HTML. It uses ASP.NET to define a Web API controller method, which performs this task and returns the number. +The following code downloads HTML from a given URL and counts the number of times the string ".NET" occurs in the HTML. The code uses ASP.NET to define a Web API controller method, which performs the task and returns the count. > [!NOTE] > If you plan on doing HTML parsing in production code, don't use regular expressions. Use a parsing library instead. :::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="ExtractDataFromNetwork"::: -Here's the same scenario written for a Universal Windows App, which performs the same task when a Button is pressed: +You can write similar code for a Universal Windows App and perform the counting task after a button press: ```csharp private readonly HttpClient _httpClient = new HttpClient(); @@ -102,13 +98,13 @@ private async void OnSeeTheDotNetsButtonClick(object sender, RoutedEventArgs e) var getDotNetFoundationHtmlTask = _httpClient.GetStringAsync("https://dotnetfoundation.org"); // Any other work on the UI thread can be done here, such as enabling a Progress Bar. - // This is important to do here, before the "await" call, so that the user - // sees the progress bar before execution of this method is yielded. + // It's important to do the extra work here before the "await" call, + // so the user sees the progress bar before execution of this method is yielded. NetworkProgressBar.IsEnabled = true; NetworkProgressBar.Visibility = Visibility.Visible; // The await operator suspends OnSeeTheDotNetsButtonClick(), returning control to its caller. - // This is what allows the app to be responsive and not block the UI thread. + // This action is what allows the app to be responsive and not block the UI thread. var html = await getDotNetFoundationHtmlTask; int count = Regex.Matches(html, @"\.NET").Count; @@ -121,80 +117,83 @@ private async void OnSeeTheDotNetsButtonClick(object sender, RoutedEventArgs e) ### Wait for multiple tasks to complete -You may find yourself in a situation where you need to retrieve multiple pieces of data concurrently. The `Task` API contains two methods, and , that allow you to write asynchronous code that performs a non-blocking wait on multiple background jobs. +In some scenarios, the code needs to retrieve multiple pieces of data concurrently. The `Task` APIs provide methods that enable you to write asynchronous code that performs a nonblocking wait on multiple background jobs: + +- method +- method -This example shows how you might grab `User` data for a set of `userId`s. +The following example shows how you might grab `User` object data for a set of `userId` objects. :::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="GetUsersForDataset"::: -Here's another way to write this more succinctly, using LINQ: +You can write this code more succinctly by using LINQ: :::code language="csharp" source="snippets/async-scenarios/Program.cs" ID="GetUsersForDatasetByLINQ"::: -Although it's less code, use caution when mixing LINQ with asynchronous code. Because LINQ uses deferred (lazy) execution, async calls won't happen immediately as they do in a `foreach` loop unless you force the generated sequence to iterate with a call to `.ToList()` or `.ToArray()`. The above example uses to perform the query eagerly and store the results in an array. That forces the code `id => GetUserAsync(id)` to run and start the task. +Although you write less code by using LINQ, exercise caution when mixing LINQ with asynchronous code. LINQ uses deferred (or lazy) execution. Asynchronous calls don't happen immediately as they do in a `foreach` loop, unless you force the generated sequence to iterate with a call to the `.ToList()` or `.ToArray()` method. This example uses the method to perform the query eagerly and store the results in an array. This approach forces the `id => GetUserAsync(id)` statement to run and initiate the task. -## Important info and advice +## Review considerations for asynchronous programming -With async programming, there are some details to keep in mind that can prevent unexpected behavior. +With asynchronous programming, there are several details to keep in mind that can prevent unexpected behavior. -* `async` **methods need to have an** `await` **keyword in their body or they will never yield!** +### Use await inside async() method body - This is important to keep in mind. If `await` is not used in the body of an `async` method, the C# compiler generates a warning, but the code compiles and runs as if it were a normal method. This is incredibly inefficient, as the state machine generated by the C# compiler for the async method is not accomplishing anything. +When you use the `async` modifier, you should include one or more `await` expressions in the method body. If the compiler doesn't encounter an `await` expression, the method fails to yield. Although the compiler generates a warning, the code still compiles and the compiler runs the method. The state machine generated by the C# compiler for the asynchronous method doesn't accomplish anything, so the entire process is highly inefficient. -* **Add "Async" as the suffix of every async method name you write.** +### Add "Async" suffix to asynchronous method names - This is the convention used in .NET to more easily differentiate synchronous and asynchronous methods. Certain methods that aren't explicitly called by your code (such as event handlers or web controller methods) don't necessarily apply. Because they are not explicitly called by your code, being explicit about their naming isn't as important. +The .NET style convention is to add the "Async" suffix to all asynchronous method names. This approach helps to more easily differentiate between synchronous and asynchronous methods. Certain methods that aren't explicitly called by your code (such as event handlers or web controller methods) don't necessarily apply in this scenario. Because these items aren't explicitly called by your code, using explicit naming isn't as important. -* `async void` **should only be used for event handlers.** +### Return 'async void' only from event handlers - `async void` is the only way to allow asynchronous event handlers to work because events do not have return types (thus cannot make use of `Task` and `Task`). Any other use of `async void` does not follow the TAP model and can be challenging to use, such as: +Event handlers must declare `void` return types and can't use or return `Task` and `Task` objects as other methods do. When you write asynchronous event handlers, you need to use the `async` modifier on a `void` returning method for the handlers. Other implementations of `async void` returning methods don't follow the TAP model and can present challenges: - * Exceptions thrown in an `async void` method can't be caught outside of that method. - * `async void` methods are difficult to test. - * `async void` methods can cause bad side effects if the caller isn't expecting them to be async. +* Exceptions thrown in an `async void` method can't be caught outside of that method +* `async void` methods are difficult to test +* `async void` methods can cause negative side effects if the caller isn't expecting them to be asynchronous -* **Tread carefully when using async lambdas in LINQ expressions** +### Use caution with asynchronous lambdas in LINQ - Lambda expressions in LINQ use deferred execution, meaning code could end up executing at a time when you're not expecting it to. The introduction of blocking tasks into this can easily result in a deadlock if not written correctly. Additionally, the nesting of asynchronous code like this can also make it more difficult to reason about the execution of the code. Async and LINQ are powerful but should be used together as carefully and clearly as possible. +It's important to use caution when you implement asynchronous lambdas in LINQ expressions. Lambda expressions in LINQ use deferred execution, which means the code can execute at an unexpected time. The introduction of blocking tasks into this scenario can easily result in a deadlock, if the code isn't written correctly. Moreover, the nesting of asynchronous code can also make it difficult to reason about the execution of the code. Async and LINQ are powerful, but these techniques should be used together as carefully and clearly as possible. -* **Write code that awaits Tasks in a non-blocking manner** +### Yield for tasks in a nonblocking manner - Blocking the current thread as a means to wait for a `Task` to complete can result in deadlocks and blocked context threads and can require more complex error-handling. The following table provides guidance on how to deal with waiting for tasks in a non-blocking way: +If your program needs the result of a task, write code that implements the `await` expression in a nonblocking manner. Blocking the current thread as a means to wait synchronously for a `Task` item to complete can result in deadlocks and blocked context threads. This programming approach can require more complex error-handling. The following table provides guidance on how access results from tasks in a nonblocking way: - | Use this... | Instead of this... | When wishing to do this... | - |----------------------|------------------------------|--------------------------------------------| - | `await` | `Task.Wait` or `Task.Result` | Retrieving the result of a background task | - | `await Task.WhenAny` | `Task.WaitAny` | Waiting for any task to complete | - | `await Task.WhenAll` | `Task.WaitAll` | Waiting for all tasks to complete | - | `await Task.Delay` | `Thread.Sleep` | Waiting for a period of time | +| Task scenario | Current code | Replace with 'await' | +| --- | --- | --- | +| _Retrieve the result of a background task_ | `Task.Wait` or `Task.Result` | `await` | +| _Continue when any task completes_ | `Task.WaitAny` | `await Task.WhenAny` | +| _Continue when **all** tasks complete_ | `Task.WaitAll` | `await Task.WhenAll` | +| _Continue after some amount of time_ | `Thread.Sleep` | `await Task.Delay` | -* **Consider using** `ValueTask` **where possible** +### Consider using ValueTask type - Returning a `Task` object from async methods can introduce performance bottlenecks in certain paths. `Task` is a reference type, so using it means allocating an object. In cases where a method declared with the `async` modifier returns a cached result or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. It can become costly if those allocations occur in tight loops. For more information, see [generalized async return types](../language-reference/keywords/async.md#return-types). +When an asynchronous method returns a `Task` object, performance bottlenecks might be introduced in certain paths. Because `Task` is a reference type, a `Task` object is allocated from the heap. If a method declared with the `async` modifier returns a cached result or completes synchronously, the extra allocations can accrue significant time costs in performance critical sections of code. This scenario can become costly when the allocations occur in tight loops. For more information, see [generalized async return types](../language-reference/keywords/async.md#return-types). -* **Consider using** `ConfigureAwait(false)` +### Understand when to set ConfigureAwait(false) - A common question is, "when should I use the method?". The method allows for a `Task` instance to configure its awaiter. This is an important consideration and setting it incorrectly could potentially have performance implications and even deadlocks. For more information on `ConfigureAwait`, see the [ConfigureAwait FAQ](https://devblogs.microsoft.com/dotnet/configureawait-faq). +Developers often inquire about when to use the boolean. This API allows for a `Task` instance to configure the context for the state machine that implements any `await` expression. When the boolean isn't set correctly, performance can degrade or deadlocks can occur. For more information, see [ConfigureAwait FAQ](https://devblogs.microsoft.com/dotnet/configureawait-faq). -* **Write less stateful code** +### Write less-stateful code - Don't depend on the state of global objects or the execution of certain methods. Instead, depend only on the return values of methods. Why? +Avoid writing code that depends on the state of global objects or the execution of certain methods. Instead, depend only on the return values of methods. There are many benefits to writing code that is less-stateful: - * Code will be easier to reason about. - * Code will be easier to test. - * Mixing async and synchronous code is far simpler. - * Race conditions can typically be avoided altogether. - * Depending on return values makes coordinating async code simple. - * (Bonus) it works really well with dependency injection. +* Easier to reason about code +* Easier to test code +* More simple to mix asynchronous and synchronous code +* Able to avoid race conditions in code +* Simple to coordinate asynchronous code that depends on return values +* (Bonus) Works well with dependency injection in code -A recommended goal is to achieve complete or near-complete [Referential Transparency](https://en.wikipedia.org/wiki/Referential_transparency_%28computer_science%29) in your code. Doing so will result in a predictable, testable, and maintainable codebase. +A recommended goal is to achieve complete or near-complete [Referential Transparency](https://en.wikipedia.org/wiki/Referential_transparency) in your code. This approach results in a predictable, testable, and maintainable codebase. -## Complete example +## Review the complete example -The following code is the complete text of the *Program.cs* file for the example. +The following code represents the complete example, which is available in the *Program.cs* example file. :::code language="csharp" source="snippets/async-scenarios/Program.cs"::: -## Other resources +## Related links -* [The Task asynchronous programming model (C#)](task-asynchronous-programming-model.md). +* [The Task asynchronous programming model (C#)](task-asynchronous-programming-model.md) diff --git a/docs/csharp/distinguish-delegates-events.md b/docs/csharp/distinguish-delegates-events.md index 7062bb34541ea..aa8d5ddee749c 100644 --- a/docs/csharp/distinguish-delegates-events.md +++ b/docs/csharp/distinguish-delegates-events.md @@ -1,93 +1,43 @@ --- title: Delegates vs. events description: Learn the difference between delegates and events and when to use each of these features of .NET Core. -ms.date: 06/20/2016 +ms.date: 03/11/2025 ms.subservice: fundamentals -ms.assetid: 0fdc8629-2fdb-4a7c-a433-5b9d04eaf911 --- - # Distinguishing Delegates and Events [Previous](modern-events.md) -Developers that are new to the .NET Core platform often struggle -when deciding between a design based on `delegates` and a design -based on `events`. The choice of delegates or events is often difficult, because the two -language features are similar. Events are even built using -the language support for delegates. +Developers that are new to the .NET platform often struggle when deciding between a design based on `delegates` and a design based on `events`. The choice of delegates or events is often difficult, because the two language features are similar. Events are even built using the language support for delegates. An event handler declaration declares a delegate type. -They both offer a late binding scenario: they enable scenarios -where a component communicates by calling a method that is only -known at run time. They both support single and multiple subscriber -methods. You may find this referred to as singlecast and multicast -support. They both support similar syntax for adding and removing -handlers. Finally, raising an event and calling a delegate use exactly the same method call syntax. They even both support the same `Invoke()` -method syntax for use with the `?.` operator. +Both offer a late binding scenario: they enable scenarios where a component communicates by calling a method that is only known at run time. They both support single and multiple subscriber methods. You might find these terms referred to as single cast and multicast support. They both support similar syntax for adding and removing handlers. Finally, raising an event and calling a delegate use exactly the same method call syntax. They even both support the same `Invoke()` method syntax for use with the `?.` operator. -With all those similarities, it is easy to have trouble determining when -to use which. +With all those similarities, it's easy to have trouble determining when to use which. ## Listening to Events is Optional -The most important consideration in determining which language feature -to use is whether or not there must be an attached subscriber. If your -code must call the code supplied by the subscriber, you should -use a design based on delegates when you need to implement callback. If your code can complete all its -work without calling any subscribers, you should use a -design based on events. - -Consider the examples built during this section. The code you built -using `List.Sort()` must be given a comparer function in order to -properly sort the elements. LINQ queries must be supplied with delegates -in order to determine what elements to return. Both used a design built -with delegates. - -Consider the `Progress` event. It reports progress on a task. -The task continues to proceed whether or not there are any listeners. -The `FileSearcher` is another example. It would still search and find -all the files that were sought, even with no event subscribers attached. -UX controls still work correctly, even when there are no subscribers -listening to the events. They both use designs based on events. +The most important consideration in determining which language feature to use is whether or not there must be an attached subscriber. If your code must call the code supplied by the subscriber, you should use a design based on delegates when you need to implement callback. If your code can complete all its work without calling any subscribers, you should use a design based on events. + +Consider the examples built during this section. The code you built using `List.Sort()` must be given a comparer function in order to properly sort the elements. LINQ queries must be supplied with delegates in order to determine what elements to return. Both used a design built with delegates. + +Consider the `Progress` event. It reports progress on a task. The task continues to proceed whether or not there are any listeners. The `FileSearcher` is another example. It would still search and find all the files that were sought, even with no event subscribers attached. UX controls still work correctly, even when there are no subscribers listening to the events. They both use designs based on events. ## Return Values Require Delegates -Another consideration is the method prototype you would want for your -delegate method. As you've seen, the delegates used for events all -have a void return type. You've also seen that there are idioms to -create event handlers that do pass information back to event sources -through modifying properties of the event argument object. While these -idioms do work, they are not as natural as returning a value from a -method. +Another consideration is the method prototype you would want for your delegate method. As you saw, the delegates used for events all have a void return type. There are idioms to create event handlers that do pass information back to event sources through modifying properties of the event argument object. While these idioms do work, they aren't as natural as returning a value from a method. -Notice that these two heuristics may often both be present: If your -delegate method returns a value, it will likely impact the algorithm -in some way. +Notice that these two heuristics can often both be present: If your delegate method returns a value, it affects the algorithm in some way. ## Events Have Private Invocation -Classes other than the one in which an event is contained can only add -and remove event listeners; only the class containing the event can -invoke the event. Events are typically public class members. -By comparison, delegates are often passed as parameters and stored as -private class members, if they are stored at all. +Classes other than the one in which an event is contained can only add and remove event listeners; only the class containing the event can invoke the event. Events are typically public class members. By comparison, delegates are often passed as parameters and stored as private class members, if they're stored at all. ## Event Listeners Often Have Longer Lifetimes -That event listeners have longer lifetimes is a slightly weaker justification. However, you may find that event-based designs are more natural when the event source will be -raising events over a long period of time. You can see examples of -event-based design for UX controls on many systems. Once you subscribe to an event, -the event source may raise events throughout the lifetime of the program. -(You can unsubscribe from events when you no longer need them.) +The longer lifetime of event listeners is a slightly weaker justification. However, you might find that event-based designs are more natural when the event source is raising events over a long period of time. You can see examples of event-based design for UX controls on many systems. Once you subscribe to an event, the event source can raise events throughout the lifetime of the program. (You can unsubscribe from events when you no longer need them.) -Contrast that with many delegate-based designs, where a delegate is -used as an argument to a method, and the delegate is not used after that -method returns. +Contrast that with many delegate-based designs, where a delegate is used as an argument to a method, and the delegate isn't used after that method returns. ## Evaluate Carefully -The above considerations are not hard and fast rules. Instead, they -represent guidance that can help you decide which choice is best for -your particular usage. Because they are similar, you can even -prototype both, and consider which would be more natural to work -with. They both handle late binding scenarios well. Use the one -that communicates your design the best. +The above considerations aren't hard and fast rules. Instead, they represent guidance that can help you decide which choice is best for your particular usage. Because they're similar, you can even prototype both, and consider which would be more natural to work with. They both handle late binding scenarios well. Use the one that communicates your design the best. diff --git a/docs/csharp/event-pattern.md b/docs/csharp/event-pattern.md index a51954e76968a..5e88b53485b65 100644 --- a/docs/csharp/event-pattern.md +++ b/docs/csharp/event-pattern.md @@ -1,18 +1,16 @@ --- title: Standard .NET event patterns description: Learn about .NET event patterns and how to create standard event sources and subscribe and process standard events in your code. -ms.date: 09/02/2022 +ms.date: 03/13/2025 ms.subservice: fundamentals -ms.assetid: 8a3133d6-4ef2-46f9-9c8d-a8ea8898e4c9 --- - # Standard .NET event patterns [Previous](events-overview.md) -.NET events generally follow a few known patterns. Standardizing on these patterns means that developers can leverage knowledge of those standard patterns, which can be applied to any .NET event program. +.NET events generally follow a few known patterns. Standardizing on these patterns means that developers can apply knowledge of those standard patterns, which can be applied to any .NET event program. -Let's go through these standard patterns so you will have all the knowledge you need to create standard event sources, and subscribe and process standard events in your code. +Let's go through these standard patterns so you have all the knowledge you need to create standard event sources, and subscribe and process standard events in your code. ## Event delegate signatures @@ -22,137 +20,104 @@ The standard signature for a .NET event delegate is: void EventRaised(object sender, EventArgs args); ``` -The return type is void. Events are based on delegates and are multicast delegates. That supports multiple subscribers for any event source. The single return value from a method doesn't scale to multiple event subscribers. Which return value does the event source see after raising an event? Later in this article you'll -see how to create event protocols that support event subscribers that report information to the event source. - -The argument list contains two arguments: the sender, and the event arguments. The compile-time type of `sender` is `System.Object`, even though you likely know a more derived type that would always be correct. By convention, use `object`. +This standard signature provides insight into when events are used: -The second argument has typically been a type that is derived from `System.EventArgs`. (You'll see in the -[next section](modern-events.md) that this convention is no longer enforced.) If your event type does not need any additional arguments, you will still provide both arguments. There is a special value, `EventArgs.Empty` that you should use to denote that your event does not contain any additional information. +- ***The return type is void***. Events may have zero to many listeners. Raising an event notifies all listeners. In general, listeners don't provide values in response to events. +- ***Events indicate the sender***: The event signature includes the object that raised the event. That provides any listener with a mechanism to communicate with the sender. The compile-time type of `sender` is `System.Object`, even though you likely know a more derived type that would always be correct. By convention, use `object`. +- ***Events package additional information in a single structure***: The `args` parameter is a type derived from that includes any additional necessary information. (You'll see in the [next section](modern-events.md) that this convention is no longer enforced.) If your event type doesn't need any more arguments, you still must provide both arguments. There's a special value, that you should use to denote that your event doesn't contain any additional information. Let's build a class that lists files in a directory, or any of its subdirectories that follow a pattern. This component raises an event for each file found that matches the pattern. -Using an event model provides some design advantages. You can create multiple event listeners that perform different actions when a sought file is found. Combining the different listeners can create more -robust algorithms. +Using an event model provides some design advantages. You can create multiple event listeners that perform different actions when a sought file is found. Combining the different listeners can create more robust algorithms. -Here is the initial event argument declaration for finding a sought -file: +Here's the initial event argument declaration for finding a sought file: -[!code-csharp[EventArgs](../../samples/snippets/csharp/events/Program.cs#EventArgsV1 "Define event arguments")] +:::code language="csharp" source="./snippets/events/VersionOne.cs" id="EventArgsV1"::: -Even though this type looks like a small, data-only type, you should follow the convention and make it a reference (`class`) type. That means the argument object will be passed by reference, and any updates to the data will be viewed by all subscribers. The first version is an immutable object. You should prefer to make the properties in your event argument type immutable. That way, one subscriber cannot change the values before another subscriber sees them. (There are exceptions to this, as you'll see below.) +Even though this type looks like a small, data-only type, you should follow the convention and make it a reference (`class`) type. That means the argument object is passed by reference, and any updates to the data are viewed by all subscribers. The first version is an immutable object. You should prefer to make the properties in your event argument type immutable. That way, one subscriber can't change the values before another subscriber sees them. (There are exceptions to this practice, as you see later.) -Next, we need to create the event declaration in the FileSearcher class. Leveraging the `EventHandler` type means that you don't need to create yet another type definition. You simply use a generic specialization. +Next, we need to create the event declaration in the FileSearcher class. Using the type means that you don't need to create yet another type definition. You just use a generic specialization. Let's fill out the FileSearcher class to search for files that match a pattern, and raise the correct event when a match is discovered. -[!code-csharp[FileSearcher](../../samples/snippets/csharp/events/Program.cs#FileSearcherV1 "Create the initial file searcher")] +:::code language="csharp" source="./snippets/events/VersionOne.cs" id="FileSearcherV1"::: ## Define and raise field-like events The simplest way to add an event to your class is to declare that event as a public field, as in the preceding example: -[!code-csharp[DeclareEvent](../../samples/snippets/csharp/events/Program.cs#DeclareEvent "Declare the file found event")] +:::code language="csharp" source="./snippets/events/Program.cs" id="DeclareEvent"::: -This looks like it's declaring a public field, which would appear to be a bad object-oriented practice. You want to protect data access through properties, or methods. While this may look like a bad practice, the code generated by the compiler does create wrappers so that the event objects can only be accessed in safe ways. The only operations available on a field-like event are add handler: +This looks like it's declaring a public field, which would appear to be a bad object-oriented practice. You want to protect data access through properties, or methods. While this code might look like a bad practice, the code generated by the compiler does create wrappers so that the event objects can only be accessed in safe ways. The only operations available on a field-like event are *add handler*: -[!code-csharp[DeclareEventHandler](../../samples/snippets/csharp/events/Program.cs#DeclareEventHandler "Declare the file found event handler")] +:::code language="csharp" source="./snippets/events/Program.cs" id="AttachEventHandler"::: -and remove handler: +And *remove handler*: -[!code-csharp[RemoveEventHandler](../../samples/snippets/csharp/events/Program.cs#RemoveHandler "Remove the event handler")] +:::code language="csharp" source="./snippets/events/Program.cs" id="DetachHandler"::: -Note that there's a local variable for the handler. If you used the body of the lambda, the remove would not work correctly. It would be a different instance of the delegate, and silently do nothing. +There's a local variable for the handler. If you used the body of the lambda, the remove wouldn't work correctly. It would be a different instance of the delegate, and silently do nothing. -Code outside the class cannot raise the event, nor can it perform any other operations. +Code outside the class can't raise the event, nor can it perform any other operations. ## Return values from event subscribers Your simple version is working fine. Let's add another feature: Cancellation. -When you raise the found event, listeners should be able to stop further processing, if this file is the last one sought. +When you raise the *Found* event, listeners should be able to stop further processing, if this file is the last one sought. -The event handlers do not return a value, so you need to communicate that in another way. The standard event pattern uses the `EventArgs` object to include fields that event subscribers can use to communicate cancel. +The event handlers don't return a value, so you need to communicate that in another way. The standard event pattern uses the `EventArgs` object to include fields that event subscribers can use to communicate cancel. -Two different patterns could be used, based on the semantics of the Cancel contract. In both cases, you'll add a boolean field to the EventArguments for the found file event. +Two different patterns could be used, based on the semantics of the Cancel contract. In both cases, you add a boolean field to the EventArguments for the found file event. -One pattern would allow any one subscriber to cancel the operation. For this pattern, the new field is initialized to `false`. Any subscriber can change it to `true`. After all subscribers have seen the event raised, the FileSearcher component examines the boolean value and takes action. +One pattern would allow any one subscriber to cancel the operation. For this pattern, the new field is initialized to `false`. Any subscriber can change it to `true`. After the raising the event for all subscribers, the FileSearcher component examines the boolean value and takes action. -The second pattern would only cancel the operation if all subscribers wanted the operation canceled. In this pattern, the new field is initialized to indicate the operation should cancel, and any subscriber could change it to indicate the operation should continue. After all subscribers have seen the event raised, the FileSearcher component examines the boolean and takes action. There is one extra step in this pattern: the component needs to know if any subscribers have seen the event. If there are no subscribers, the field would -indicate a cancel incorrectly. +The second pattern would only cancel the operation if all subscribers wanted the operation canceled. In this pattern, the new field is initialized to indicate the operation should cancel, and any subscriber could change it to indicate the operation should continue. After all subscribers process the raised the event, the FileSearcher component examines the boolean and takes action. There's one extra step in this pattern: the component needs to know if any subscribers responded to the event. If there are no subscribers, the field would indicate a cancel incorrectly. Let's implement the first version for this sample. You need to add a boolean field named `CancelRequested` to the `FileFoundArgs` type: -[!code-csharp[EventArgs](../../samples/snippets/csharp/events/Program.cs#EventArgs "Update event arguments")] +:::code language="csharp" source="./snippets/events/Program.cs" id="EventArgs"::: -This new field is automatically initialized to `false`, the default value for a `Boolean` field, so you don't cancel accidentally. The only other change to the component is to check the flag after raising the event to see if any of the subscribers have requested a cancellation: +This new field is automatically initialized to `false` so you don't cancel accidentally. The only other change to the component is to check the flag after raising the event to see if any of the subscribers requested a cancellation: -```csharp -private void SearchDirectory(string directory, string searchPattern) -{ - foreach (var file in Directory.EnumerateFiles(directory, searchPattern)) - { - FileFoundArgs args = RaiseFileFound(file); - if (args.CancelRequested) - { - break; - } - } -} - -private FileFoundArgs RaiseFileFound(string file) -{ - var args = new FileFoundArgs(file); - FileFound?.Invoke(this, args); - return args; -} -``` +:::code language="csharp" source="./snippets/events/Program.cs" id="SearchWithCancel"::: -One advantage of this pattern is that it isn't a breaking change. None of the subscribers requested cancellation before, and they still are not. None of the subscriber code needs updating unless they want to -support the new cancel protocol. It's very loosely coupled. +One advantage of this pattern is that it isn't a breaking change. None of the subscribers requested cancellation before, and they still aren't. None of the subscriber code requires updates unless they want to support the new cancel protocol. -Let's update the subscriber so that it requests a cancellation once -it finds the first executable: +Let's update the subscriber so that it requests a cancellation once it finds the first executable: -```csharp -EventHandler onFileFound = (sender, eventArgs) => -{ - Console.WriteLine(eventArgs.FoundFile); - eventArgs.CancelRequested = true; -}; -``` +:::code language="csharp" source="./snippets/events/Program.cs" id="CancelSearch"::: ## Adding another event declaration Let's add one more feature, and demonstrate other language idioms for events. Let's add an overload of the `Search` method that traverses all subdirectories in search of files. -This could get to be a lengthy operation in a directory with many sub-directories. Let's add an event that gets raised when each new directory search begins. This enables subscribers to track progress, and update the user as to progress. All the samples you've created so far are public. Let's make this one an internal event. That means you can also make the types used for the arguments internal as well. +This method could get to be a lengthy operation in a directory with many subdirectories. Let's add an event that gets raised when each new directory search begins. This event enables subscribers to track progress, and update the user as to progress. All the samples you created so far are public. Let's make this event an internal event. That means you can also make the argument types internal as well. -You'll start by creating the new EventArgs derived class for reporting the new directory and progress. +You start by creating the new EventArgs derived class for reporting the new directory and progress. -[!code-csharp[DirEventArgs](../../samples/snippets/csharp/events/Program.cs#SearchDirEventArgs "Define search directory event arguments")] +:::code language="csharp" source="./snippets/events/Program.cs" id="SearchDirEventArgs"::: Again, you can follow the recommendations to make an immutable reference type for the event arguments. -Next, define the event. This time, you'll use a different syntax. In addition to using the field syntax, you can explicitly create the property, with add and remove handlers. In this sample, you won't -need extra code in those handlers, but this shows how you would create them. +Next, define the event. This time, you use a different syntax. In addition to using the field syntax, you can explicitly create the event property with add and remove handlers. In this sample, you don't need extra code in those handlers, but this shows how you would create them. -[!code-csharp[Declare event with add and remove handlers](../../samples/snippets/csharp/events/Program.cs#DeclareSearchEvent "Declare the event with add and remove handlers")] +:::code language="csharp" source="./snippets/events/Program.cs" id="DeclareSearchEvent"::: -In many ways, the code you write here mirrors the code the compiler generates for the field event definitions you've seen earlier. You create the event using syntax very similar to that used for [properties](programming-guide/classes-and-structs/properties.md). Notice that the handlers have different names: `add` and `remove`. These are called to subscribe to the event, or unsubscribe from the event. Notice that you also must declare a private backing field to store the event variable. It is initialized to null. +In many ways, the code you write here mirrors the code the compiler generates for the field event definitions you saw earlier. You create the event using syntax similar to [properties](programming-guide/classes-and-structs/properties.md). Notice that the handlers have different names: `add` and `remove`. These accessors are called to subscribe to the event, or unsubscribe from the event. Notice that you also must declare a private backing field to store the event variable. This variable is initialized to null. -Next, let's add the overload of the `Search` method that traverses subdirectories and raises both events. The easiest way to accomplish this is to use a default argument to specify that you want to search all directories: +Next, let's add the overload of the `Search` method that traverses subdirectories and raises both events. The easiest way is to use a default argument to specify that you want to search all directories: -[!code-csharp[SearchImplementation](../../samples/snippets/csharp/events/Program.cs#FinalImplementation "Implementation to search directories")] +:::code language="csharp" source="./snippets/events/Program.cs" id="FinalImplementation"::: -At this point, you can run the application calling the overload for searching all sub-directories. There are no subscribers on the new `DirectoryChanged` event, but using the `?.Invoke()` idiom ensures that this works correctly. +At this point, you can run the application calling the overload for searching all subdirectories. There are no subscribers on the new `DirectoryChanged` event, but using the `?.Invoke()` idiom ensures it works correctly. Let's add a handler to write a line that shows the progress in the console window. -[!code-csharp[Search](../../samples/snippets/csharp/events/Program.cs#Search "Declare event handler")] +:::code language="csharp" source="./snippets/events/Program.cs" id="Search"::: -You've seen patterns that are followed throughout the .NET ecosystem. By learning these patterns and conventions, you'll be writing idiomatic C# and .NET quickly. +You saw patterns that are followed throughout the .NET ecosystem. By learning these patterns and conventions, you're writing idiomatic C# and .NET quickly. ## See also @@ -160,7 +125,7 @@ You've seen patterns that are followed throughout the .NET ecosystem. By learnin - [Event design](../standard/design-guidelines/event.md) - [Handle and raise events](../standard/events/index.md) -Next, you'll see some changes in these patterns in the most recent release of .NET. +Next, you see some changes in these patterns in the most recent release of .NET. > [!div class="step-by-step"] > [Next](modern-events.md) diff --git a/docs/csharp/events-overview.md b/docs/csharp/events-overview.md index 0300b13a0b966..7b28d04cb3275 100644 --- a/docs/csharp/events-overview.md +++ b/docs/csharp/events-overview.md @@ -1,112 +1,60 @@ --- title: Introduction to events -description: Learn about events in .NET Core and our language design goals for events in this overview. -ms.date: 06/20/2016 -ms.assetid: 9b8d2a00-1584-4a5b-8994-5003d54d8e0c +description: Learn about events in .NET and our language design goals for events in this overview. +ms.date: 03/11/2025 --- - # Introduction to events [Previous](delegates-patterns.md) -Events are, like delegates, a *late binding* mechanism. In fact, -events are built on the language support for delegates. +Events are, like delegates, a *late binding* mechanism. In fact, events are built on the language support for delegates. -Events are a way for an object to broadcast (to all interested -components in the system) that something has happened. Any other -component can subscribe to the event, and be notified when an event -is raised. +Events are a way for an object to broadcast (to all interested components in the system) that something happened. Any other component can subscribe to the event, and be notified when an event is raised. -You've probably used events in some of your programming. Many graphical -systems have an event model to report user interaction. These events would -report mouse movement, button presses and similar interactions. That's one -of the most common, but certainly not the only scenario where events are -used. +You probably used events in some of your programming. Many graphical systems have an event model to report user interaction. These events would report mouse movement, button presses, and similar interactions. That's one of the most common, but not the only scenario where events are used. -You can define events that should be raised for your classes. One important -consideration when working with events is that there may not be any -object registered for a particular event. You must write your code so that -it does not raise events when no listeners are configured. +You can define events that should be raised for your classes. One important consideration when working with events is that there might not be any object registered for a particular event. You must write your code so that it doesn't raise events when no listeners are configured. -Subscribing to an event also creates a coupling between two objects (the event -source, and the event sink). You need to ensure that the event sink unsubscribes -from the event source when no longer interested in events. +Subscribing to an event also creates a coupling between two objects (the event source, and the event sink). You need to ensure that the event sink unsubscribes from the event source when no longer interested in events. ## Design goals for event support The language design for events targets these goals: -- Enable very minimal coupling between an event source and an event sink. These two components may not be written by the same organization, and may even be updated on totally different schedules. - -- It should be very simple to subscribe to an event, and to unsubscribe from that same event. - +- Enable minimal coupling between an event source and an event sink. These two components might be written by different organizations, and might even be updated on different schedules. +- It should be simple to subscribe to an event, and to unsubscribe from that same event. - Event sources should support multiple event subscribers. It should also support having no event subscribers attached. -You can see that the goals for events are very similar to the goals for delegates. -That's why the event language support is built on the delegate language support. +You can see that the goals for events are similar to the goals for delegates. That's why the event language support is built on the delegate language support. ## Language support for events -The syntax for defining events, and subscribing or unsubscribing from events is -an extension of the syntax for delegates. - -To define an event you use the `event` keyword: - -```csharp -public event EventHandler Progress; -``` - -The type of the event (`EventHandler` in this example) must be a -delegate type. There are a number of conventions that you should follow -when declaring an event. Typically, the event delegate type has a void return. -Event declarations should be a verb, or a verb phrase. -Use past tense when -the event reports something that has happened. Use a present tense verb (for -example, `Closing`) to report something that is about to happen. Often, using -present tense indicates that your class supports some kind of customization -behavior. One of the most common scenarios is to support cancellation. For example, -a `Closing` event may include an argument that would indicate if the close -operation should continue, or not. Other scenarios may enable callers to modify -behavior by updating properties of the event arguments. You may raise an -event to indicate a proposed next action an algorithm will take. The event -handler may mandate a different action by modifying properties of the event -argument. - -When you want to raise the event, you call the event handlers using the delegate invocation -syntax: - -```csharp -Progress?.Invoke(this, new FileListArgs(file)); -``` - -As discussed in the section on [delegates](delegates-patterns.md), the ?. -operator makes it easy to ensure that you do not attempt to raise the event -when there are no subscribers to that event. +The syntax for defining events, and subscribing or unsubscribing from events is an extension of the syntax for delegates. -You subscribe to an event by using the `+=` operator: +You use the `event` keyword to define an event: + +:::code language="csharp" source="./snippets/events/VersionOne.cs" id="DeclareEvent"::: -```csharp -EventHandler onProgress = (sender, eventArgs) => - Console.WriteLine(eventArgs.FoundFile); +The type of the event (`EventHandler` in this example) must be a delegate type. There are conventions that you should follow when declaring an event. Typically, the event delegate type has a void return. Event declarations should be a verb, or a verb phrase. Use past tense when the event reports something that happened. Use a present tense verb (for example, `Closing`) to report something that is about to happen. Often, using present tense indicates that your class supports some kind of customization behavior. One of the most common scenarios is to support cancellation. For example, a `Closing` event can include an argument that would indicate if the close operation should continue, or not. Other scenarios enable callers to modify behavior by updating properties of the event arguments. You can raise an event to indicate a proposed next action an algorithm will take. The event handler might mandate a different action by modifying properties of the event argument. + +When you want to raise the event, you call the event handlers using the delegate invocation syntax: + +:::code language="csharp" source="./snippets/events/VersionOne.cs" id="InvokeEvent"::: + +As discussed in the section on [delegates](delegates-patterns.md), the `?.` operator makes it easy to ensure that you don't attempt to raise the event when there are no subscribers to that event. + +You subscribe to an event by using the `+=` operator: -fileLister.Progress += onProgress; -``` +:::code language="csharp" source="./snippets/events/Program.cs" id="AttachEventHandler"::: -The handler method typically has the prefix 'On' followed -by the event name, as shown above. +The handler method typically has the prefix 'On' followed by the event name, as shown in the preceding code. You unsubscribe using the `-=` operator: -```csharp -fileLister.Progress -= onProgress; -``` +:::code language="csharp" source="./snippets/events/Program.cs" id="DetachHandler"::: -It's important that you declare a local variable for the expression that -represents the event handler. That ensures the unsubscribe removes the handler. -If, instead, you used the body of the lambda expression, you are attempting -to remove a handler that has never been attached, which does nothing. +It's important that you declare a local variable for the expression that represents the event handler. That ensures the unsubscribe removes the handler. If, instead, you used the body of the lambda expression, you're attempting to remove a handler that was never attached, which does nothing. -In the next article, you'll learn more about typical event patterns, and -different variations on this example. +In the next article, you'll learn more about typical event patterns, and different variations on this example. [Next](event-pattern.md) diff --git a/docs/csharp/modern-events.md b/docs/csharp/modern-events.md index 3eeae3c79a2d6..50757132d7b06 100644 --- a/docs/csharp/modern-events.md +++ b/docs/csharp/modern-events.md @@ -1,129 +1,43 @@ --- title: The Updated .NET Core Event Pattern description: Learn how the .NET Core event pattern enables flexibility with backwards compatibility and how to implement safe event processing with async subscribers. -ms.date: 06/20/2016 +ms.date: 03/11/2025 ms.subservice: fundamentals -ms.assetid: 9aa627c3-3222-4094-9ca8-7e88e1071e06 --- - # The Updated .NET Core Event Pattern [Previous](event-pattern.md) -The previous article discussed the most common event patterns. .NET -Core has a more relaxed pattern. In this version, the -`EventHandler` definition no longer has the constraint that -`TEventArgs` must be a class derived from `System.EventArgs`. - -This increases flexibility for you, and is backwards compatible. Let's -start with the flexibility. The class System.EventArgs introduces one -method: `MemberwiseClone()`, which creates a shallow copy of the object. -That method must use reflection in order to implement -its functionality for any class derived from `EventArgs`. That -functionality is easier to create in a specific derived class. That -effectively means that deriving from System.EventArgs is a constraint -that limits your designs, but does not provide any additional benefit. -In fact, you can change the definitions of `FileFoundArgs` and -`SearchDirectoryArgs` so that they do not derive from `EventArgs`. -The program will work exactly the same. +The previous article discussed the most common event patterns. .NET Core has a more relaxed pattern. In this version, the `EventHandler` definition no longer has the constraint that `TEventArgs` must be a class derived from `System.EventArgs`. + +This increases flexibility for you, and is backwards compatible. Let's start with the flexibility. The implementation for uses a method defiend in one method: , which creates a shallow copy of the object. That method must use reflection in order to implement its functionality for any class derived from `EventArgs`. That functionality is easier to create in a specific derived class. That effectively means that deriving from System.EventArgs is a constraint that limits your designs, but doesn't provide any extra benefit. In fact, you can change the definitions of `FileFoundArgs` and `SearchDirectoryArgs` so that they don't derive from `EventArgs`. The program works exactly the same. You could also change the `SearchDirectoryArgs` to a struct, if you make one more change: -```csharp -internal struct SearchDirectoryArgs -{ - internal string CurrentSearchDirectory { get; } - internal int TotalDirs { get; } - internal int CompletedDirs { get; } - - internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this() - { - CurrentSearchDirectory = dir; - TotalDirs = totalDirs; - CompletedDirs = completedDirs; - } -} -``` - -The additional change is to call the parameterless constructor before -entering the constructor that initializes all the fields. Without -that addition, the rules of C# would report that the properties are -being accessed before they have been assigned. - -You should not change the `FileFoundArgs` from a class (reference -type) to a struct (value type). That's because the protocol for -handling cancel requires that the event arguments are passed by reference. If you made the same change, the file search class could -never observe any changes made by any of the event subscribers. A new -copy of the structure would be used for each subscriber, and that -copy would be a different copy than the one seen by the file search -object. - -Next, let's consider how this change can be backwards compatible. -The removal of the constraint does not affect any existing code. Any -existing event argument types do still derive from `System.EventArgs`. -Backwards compatibility is one major reason why they will continue -to derive from `System.EventArgs`. Any existing event subscribers will -be subscribers to an event that followed the classic pattern. - -Following similar logic, any event argument type created now would -not have any subscribers in any existing codebases. New event types -that do not derive from `System.EventArgs` will not break those -codebases. +:::code language="csharp" source="./snippets/events/FinalUpdates.cs" id="StructEventArgs"::: + +The extra change is to call the parameterless constructor before entering the constructor that initializes all the fields. Without that addition, the rules of C# would report that the properties are being accessed before being assigned. + +You shouldn't change the `FileFoundArgs` from a class (reference type) to a struct (value type). The protocol for handling cancel requires that you pass event arguments by reference. If you made the same change, the file search class could never observe any changes made by any of the event subscribers. A new copy of the structure would be used for each subscriber, and that copy would be a different copy than the one seen by the file search object. + +Next, let's consider how this change can be backwards compatible. The removal of the constraint doesn't affect any existing code. Any existing event argument types do still derive from `System.EventArgs`. Backwards compatibility is one major reason why they continue to derive from `System.EventArgs`. Any existing event subscribers are subscribers to an event that followed the classic pattern. + +Following similar logic, any event argument type created now wouldn't have any subscribers in any existing codebases. New event types that don't derive from `System.EventArgs` doesn't break those codebases. ## Events with Async subscribers -You have one final pattern to learn: How to correctly write event -subscribers that call async code. The challenge is described in -the article on [async and await](asynchronous-programming/index.md). Async methods can -have a void return type, but that is strongly discouraged. When your -event subscriber code calls an async method, you have no choice but -to create an `async void` method. The event handler signature requires -it. - -You need to reconcile this opposing guidance. Somehow, you must -create a safe `async void` method. The basics of the pattern you need -to implement are below: - -```csharp -worker.StartWorking += async (sender, eventArgs) => -{ - try - { - await DoWorkAsync(); - } - catch (Exception e) - { - //Some form of logging. - Console.WriteLine($"Async task failure: {e.ToString()}"); - // Consider gracefully, and quickly exiting. - } -}; -``` - -First, notice that the handler is marked as an async handler. Because -it is being assigned to an event handler delegate type, it will have -a void return type. That means you must follow the pattern shown in the -handler, and not allow any exceptions to be thrown out of the context -of the async handler. Because it does not return a task, there is no -task that can report the error by entering the faulted state. Because -the method is async, the method can't simply throw the exception. (The -calling method has continued execution because it is `async`.) The -actual runtime behavior will be defined differently for different -environments. It may terminate the thread or the process that owns the thread, -or leave the process in an indeterminate state. All of these potential outcomes are highly undesirable. - -That's why you should wrap the await statement for the async Task -in your own try block. If it does cause a faulted task, you can -log the error. If it is an error from which your application cannot -recover, you can exit the program quickly and gracefully - -Those are the major updates to the .NET event pattern. You will see many -examples of the earlier versions in the libraries you work with. However, -you should understand what the latest patterns are as well. - -The next article in this series helps you distinguish between using -`delegates` and `events` in your designs. They are similar concepts, -and that article will help you make the best decision for your -programs. +You have one final pattern to learn: How to correctly write event subscribers that call async code. The challenge is described in the article on [async and await](asynchronous-programming/index.md). Async methods can have a void return type, but that is discouraged. When your event subscriber code calls an async method, you have no choice but to create an `async void` method. The event handler signature requires it. + +You need to reconcile this opposing guidance. Somehow, you must create a safe `async void` method. The basics of the pattern you need to implement are shown in the following code: + +:::code language="csharp" source="./snippets/events/FinalUpdates.cs" id="AsyncEvent"::: + +First, notice that the handler is marked as an async handler. Because it's being assigned to an event handler delegate type, it has a void return type. That means you must follow the pattern shown in the handler, and not allow any exceptions to be thrown out of the context of the async handler. Because it doesn't return a task, there's no task that can report the error by entering the faulted state. Because the method is async, the method can't throw the exception. (The calling method continues execution because it's `async`.) The actual runtime behavior is defined differently for different environments. It might terminate the thread or the process that owns the thread, or leave the process in an indeterminate state. All of these potential outcomes are highly undesirable. + +You should wrap the `await` expression for the async Task in your own try block. If it does cause a faulted task, you can log the error. If it's an error from which your application can't recover, you can exit the program quickly and gracefully + +This article explained the major updates to the .NET event pattern. You might see many examples of the earlier versions in the libraries you work with. However, you should understand what the latest patterns are as well. You can see the finished code for the sample in our [Samples repository](https://github.com/dotnet/docs/blob/main/samples/snippets/csharp/events/Program.cs) on GitHub. + +The next article in this series helps you distinguish between using `delegates` and `events` in your designs. They're similar concepts, and that article helps you make the best decision for your programs. [Next](distinguish-delegates-events.md) diff --git a/docs/csharp/snippets/events/FinalUpdates.cs b/docs/csharp/snippets/events/FinalUpdates.cs new file mode 100644 index 0000000000000..6162360381e66 --- /dev/null +++ b/docs/csharp/snippets/events/FinalUpdates.cs @@ -0,0 +1,51 @@ + +namespace FinalUpdates; + +// +internal struct SearchDirectoryArgs +{ + internal string CurrentSearchDirectory { get; } + internal int TotalDirs { get; } + internal int CompletedDirs { get; } + + internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this() + { + CurrentSearchDirectory = dir; + TotalDirs = totalDirs; + CompletedDirs = completedDirs; + } +} +// + +public class W +{ + public event EventHandler StartWorking = null!; +} + +public class Unused +{ + private static void Example() + { + var worker = new W(); + + // + worker.StartWorking += async (sender, eventArgs) => + { + try + { + await DoWorkAsync(); + } + catch (Exception e) + { + //Some form of logging. + Console.WriteLine($"Async task failure: {e.ToString()}"); + // Consider gracefully, and quickly exiting. + } + }; + // + + } + + private static async Task DoWorkAsync() => await Task.Yield(); + +} diff --git a/docs/csharp/snippets/events/NuGet.Config b/docs/csharp/snippets/events/NuGet.Config new file mode 100644 index 0000000000000..95143bd09a803 --- /dev/null +++ b/docs/csharp/snippets/events/NuGet.Config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/csharp/snippets/events/Program.cs b/docs/csharp/snippets/events/Program.cs new file mode 100644 index 0000000000000..8e1811eea52c8 --- /dev/null +++ b/docs/csharp/snippets/events/Program.cs @@ -0,0 +1,118 @@ +// +var fileLister = new FileSearcher(); +int filesFound = 0; + +EventHandler onFileFound = (sender, eventArgs) => +{ + Console.WriteLine(eventArgs.FoundFile); + filesFound++; +}; + +fileLister.FileFound += onFileFound; +// + +// +fileLister.DirectoryChanged += (sender, eventArgs) => +{ + Console.Write($"Entering '{eventArgs.CurrentSearchDirectory}'."); + Console.WriteLine($" {eventArgs.CompletedDirs} of {eventArgs.TotalDirs} completed..."); +}; +// + +fileLister.Search(".", "*.dll", true); + +// +fileLister.FileFound -= onFileFound; +// + +LocalUnusedCode(); +void LocalUnusedCode() +{ + // + EventHandler onFileFound = (sender, eventArgs) => + { + Console.WriteLine(eventArgs.FoundFile); + eventArgs.CancelRequested = true; + }; + // +} + +// +public class FileFoundArgs : EventArgs +{ + public string FoundFile { get; } + public bool CancelRequested { get; set; } + + public FileFoundArgs(string fileName) => FoundFile = fileName; +} +// + +// +internal class SearchDirectoryArgs : EventArgs +{ + internal string CurrentSearchDirectory { get; } + internal int TotalDirs { get; } + internal int CompletedDirs { get; } + + internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) + { + CurrentSearchDirectory = dir; + TotalDirs = totalDirs; + CompletedDirs = completedDirs; + } +} +// + +public class FileSearcher +{ + // + public event EventHandler? FileFound; + // + + // + internal event EventHandler DirectoryChanged + { + add { _directoryChanged += value; } + remove { _directoryChanged -= value; } + } + private EventHandler? _directoryChanged; + // + + // + public void Search(string directory, string searchPattern, bool searchSubDirs = false) + { + if (searchSubDirs) + { + var allDirectories = Directory.GetDirectories(directory, "*.*", SearchOption.AllDirectories); + var completedDirs = 0; + var totalDirs = allDirectories.Length + 1; + foreach (var dir in allDirectories) + { + _directoryChanged?.Invoke(this, new (dir, totalDirs, completedDirs++)); + // Search 'dir' and its subdirectories for files that match the search pattern: + SearchDirectory(dir, searchPattern); + } + // Include the Current Directory: + _directoryChanged?.Invoke(this, new (directory, totalDirs, completedDirs++)); + SearchDirectory(directory, searchPattern); + } + else + { + SearchDirectory(directory, searchPattern); + } + } + + // + private void SearchDirectory(string directory, string searchPattern) + { + foreach (var file in Directory.EnumerateFiles(directory, searchPattern)) + { + var args = new FileFoundArgs(file); + FileFound?.Invoke(this, args); + if (args.CancelRequested) + break; + } + } + // + // +} diff --git a/docs/csharp/snippets/events/VersionOne.cs b/docs/csharp/snippets/events/VersionOne.cs new file mode 100644 index 0000000000000..69ffca84ca581 --- /dev/null +++ b/docs/csharp/snippets/events/VersionOne.cs @@ -0,0 +1,30 @@ +namespace events; + +// +public class FileFoundArgs : EventArgs +{ + public string FoundFile { get; } + + public FileFoundArgs(string fileName) => FoundFile = fileName; +} +// + +// +public class FileSearcher +{ + // + public event EventHandler? FileFound; + // + + public void Search(string directory, string searchPattern) + { + foreach (var file in Directory.EnumerateFiles(directory, searchPattern)) + { + // + FileFound?.Invoke(this, new FileFoundArgs(file)); + // + } + } +} +// + diff --git a/samples/snippets/csharp/events/events.csproj b/docs/csharp/snippets/events/events.csproj similarity index 65% rename from samples/snippets/csharp/events/events.csproj rename to docs/csharp/snippets/events/events.csproj index f704bf4988fa6..16829f3e536d6 100644 --- a/samples/snippets/csharp/events/events.csproj +++ b/docs/csharp/snippets/events/events.csproj @@ -1,8 +1,8 @@ - + + net9.0 Exe - net8.0 enable enable diff --git a/samples/snippets/csharp/events/Program.cs b/samples/snippets/csharp/events/Program.cs deleted file mode 100644 index dba5115159bc8..0000000000000 --- a/samples/snippets/csharp/events/Program.cs +++ /dev/null @@ -1,159 +0,0 @@ - using System.IO; - -namespace EventSampleCode -{ - class Program - { - static void Main(string[] args) - { - // - var fileLister = new FileSearcher(); - int filesFound = 0; - - EventHandler onFileFound = (sender, eventArgs) => - { - Console.WriteLine(eventArgs.FoundFile); - filesFound++; - }; - - fileLister.FileFound += onFileFound; - // - - // - fileLister.DirectoryChanged += (sender, eventArgs) => - { - Console.Write($"Entering '{eventArgs.CurrentSearchDirectory}'."); - Console.WriteLine($" {eventArgs.CompletedDirs} of {eventArgs.TotalDirs} completed..."); - }; - // - - fileLister.Search(".", "*.dll", true); - - // - fileLister.FileFound -= onFileFound; - // - } - } - - // - public class FileFoundArgs : EventArgs - { - public string FoundFile { get; } - public bool CancelRequested { get; set; } - - public FileFoundArgs(string fileName) => FoundFile = fileName; - } - // - - // - internal class SearchDirectoryArgs : EventArgs - { - internal string CurrentSearchDirectory { get; } - internal int TotalDirs { get; } - internal int CompletedDirs { get; } - - internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) - { - CurrentSearchDirectory = dir; - TotalDirs = totalDirs; - CompletedDirs = completedDirs; - } - } - // - - public class FileSearcher - { - // - public event EventHandler? FileFound; - // - // - internal event EventHandler DirectoryChanged - { - add { _directoryChanged += value; } - remove { _directoryChanged -= value; } - } - private EventHandler? _directoryChanged; - // - - // - public void Search(string directory, string searchPattern, bool searchSubDirs = false) - { - if (searchSubDirs) - { - var allDirectories = Directory.GetDirectories(directory, "*.*", SearchOption.AllDirectories); - var completedDirs = 0; - var totalDirs = allDirectories.Length + 1; - foreach (var dir in allDirectories) - { - RaiseSearchDirectoryChanged(dir, totalDirs, completedDirs++); - // Search 'dir' and its subdirectories for files that match the search pattern: - SearchDirectory(dir, searchPattern); - } - // Include the Current Directory: - RaiseSearchDirectoryChanged(directory, totalDirs, completedDirs++); - - SearchDirectory(directory, searchPattern); - } - else - { - SearchDirectory(directory, searchPattern); - } - } - - private void SearchDirectory(string directory, string searchPattern) - { - foreach (var file in Directory.EnumerateFiles(directory, searchPattern)) - { - FileFoundArgs args = RaiseFileFound(file); - if (args.CancelRequested) - { - break; - } - } - } - - private void RaiseSearchDirectoryChanged( - string directory, int totalDirs, int completedDirs) => - _directoryChanged?.Invoke( - this, - new SearchDirectoryArgs(directory, totalDirs, completedDirs)); - - private FileFoundArgs RaiseFileFound(string file) - { - var args = new FileFoundArgs(file); - FileFound?.Invoke(this, args); - return args; - } - // - } -} - -namespace VersionOne -{ - // - public class FileFoundArgs : EventArgs - { - public string FoundFile { get; } - - public FileFoundArgs(string fileName) => FoundFile = fileName; - } - // - - // - public class FileSearcher - { - public event EventHandler? FileFound; - - public void Search(string directory, string searchPattern) - { - foreach (var file in Directory.EnumerateFiles(directory, searchPattern)) - { - RaiseFileFound(file); - } - } - - private void RaiseFileFound(string file) => - FileFound?.Invoke(this, new FileFoundArgs(file)); - } - // -} diff --git a/samples/snippets/csharp/events/README.md b/samples/snippets/csharp/events/README.md deleted file mode 100644 index 284af494b29d4..0000000000000 --- a/samples/snippets/csharp/events/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# C# Events Sample - -This sample is created during the [Delegates and Events topic](https://learn.microsoft.com/dotnet/csharp/delegates-events) -for learning C# features. Please see that topic for detailed steps on the code -for this sample. - -## Key Features - -This sample demonstrates defining and raising events, subscribing to -events, and release event handlers. - -## Build and Run - -To build and run the sample, type the following two commands: - -```dotnetcli -dotnet restore -dotnet run -``` - -`dotnet restore` restores the dependencies for this sample. - -`dotnet run` builds the sample and runs the output assembly. - -**Note:** Starting with .NET Core 2.0 SDK, you don't have to run [`dotnet restore`](https://learn.microsoft.com/dotnet/core/tools/dotnet-restore) because it's run implicitly by all commands that require a restore to occur, such as `dotnet new`, `dotnet build` and `dotnet run`. -It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as [continuous integration builds in Azure DevOps Services](https://learn.microsoft.com/azure/devops/build-release/apps/aspnet/build-aspnet-core) or in build systems that need to explicitly control the time at which the restore occurs.