diff --git a/.openpublishing.redirection.ai.json b/.openpublishing.redirection.ai.json index fc3dc05eff4b4..f007f29d0a3a2 100644 --- a/.openpublishing.redirection.ai.json +++ b/.openpublishing.redirection.ai.json @@ -9,10 +9,6 @@ "redirect_url": "/dotnet/ai/microsoft-extensions-ai", "redirect_document_id": true }, - { - "source_path_from_root": "/docs/ai/conceptual/agents.md", - "redirect_url": "/dotnet/ai" - }, { "source_path_from_root": "/docs/ai/conceptual/evaluation-libraries.md", "redirect_url": "/dotnet/ai/evaluation/libraries", diff --git a/docfx.json b/docfx.json index 5a4bc914e7e25..a5cb54230529a 100644 --- a/docfx.json +++ b/docfx.json @@ -365,7 +365,7 @@ "docs/core/project-sdk/**/**.md": "gewarren", "docs/core/runtime-config/**/**.md": "gewarren", "docs/core/testing/**/**.md": "IEvangelist", - "docs/core/tools/**/**.md": "adegeo", + "docs/core/tools/**/**.md": "meaghanlewis", "docs/core/tutorials/**/**.md": "meaghanlewis", "docs/core/versions/**/**.md": "billwagner", "docs/core/whats-new/**/**.md": "gewarren", @@ -458,7 +458,7 @@ "docs/core/project-sdk/**/**.md": "gewarren", "docs/core/runtime-config/**/**.md": "gewarren", "docs/core/testing/**/**.md": "dapine", - "docs/core/tools/**/**.md": "adegeo", + "docs/core/tools/**/**.md": "mosagie", "docs/core/tutorials/**/**.md": "mosagie", "docs/core/versions/**/**.md": "wiwagn", "docs/core/whats-new/**/**.md": "gewarren", diff --git a/docs/ai/conceptual/agents.md b/docs/ai/conceptual/agents.md new file mode 100644 index 0000000000000..91b66d6ca8273 --- /dev/null +++ b/docs/ai/conceptual/agents.md @@ -0,0 +1,96 @@ +--- +title: Agents +description: Introduction to agents +author: luisquintanilla +ms.author: luquinta +ms.date: 10/01/2025 +ms.topic: concept-article +--- + +# Agents + +This article introduces the core concepts behind agents, why they matter, and how they fit into workflows, setting you up to get started building agents in .NET. + +## What are agents? + +**Agents are systems that accomplish objectives.** + +![Components of an agent](../media/agents/agent-components.png) + +Agents become more capable when equipped with the following: + +- **Reasoning and decision-making**: Powered by LLMs, search algorithms, or planning and decision-making systems. +- **Tool usage**: Access to Model Context Protocol (MCP) servers, code execution, and external APIs. +- **Context awareness**: Informed by chat history, threads, vector stores, enterprise data, or knowledge graphs. + +These capabilities allow agents to operate more autonomously, adaptively, and intelligently. + +## What are workflows? + +As objectives grow in complexity, they need to be broken down into manageable steps. That's where workflows come in. + +**Workflows define the sequence of steps required to achieve an objective.** + +Imagine you're launching a new feature on your business website. If it's a simple update, you might go from idea to production in a few hours. But for more complex initiatives, the process might include: + +- Requirement gathering +- Design and architecture +- Implementation +- Testing +- Deployment + +A few important observations: + +- Each step might contain subtasks. +- Different specialists might own different phases. +- Progress isn’t always linear. Bugs found during testing might send you back to implementation. +- Success depends on planning, orchestration, and communication across stakeholders. + +### Agents + workflows = agentic workflows + +Workflows don't require agents, but agents can supercharge them. + +When agents are equipped with reasoning, tools, and context, they can optimize workflows. + +This is the foundation of multi-agent systems, where agents collaborate within workflows to achieve complex goals. + +### Workflow orchestration + +Agentic workflows can be orchestrated in a variety of ways. The following are a few of the most common: + +- [Sequential](#sequential) +- [Concurrent](#concurrent) +- [Handoff](#handoff) +- [Group chat](#group-chat) + +#### Sequential + +Agents process tasks one after another, passing results forward. + +![Sequential agent orchestration: Task Input → Agent A → Agent B → Agent C → Final Output](../media/agents/sequential-workflow.png) + +#### Concurrent + +Agents work in parallel, each handling different aspects of the task. + +![Concurrent agent orchestration: Task Input → Agents A, B, C → Aggregate Results → Final Output](../media/agents/concurrent-workflow.png) + +#### Handoff + +Responsibility shifts from one agent to another based on conditions or outcomes. + +![Handoff orchestration: Task Input → Agent A Decision → Agent B or Agent A → Agent B Decision → Agent C or Agent B → Final Output](../media/agents/handoff-workflow.png) + +#### Group chat + +Agents collaborate in a shared conversation, exchanging insights in real-time. + +![Group chat orchestration: User and Agents A, B, C collaborate via GroupChat to produce final output](../media/agents/groupchat-workflow.png) + +## How can I get started building agents in .NET? + +The building blocks in and supply the foundations for agents by providing modular components for AI models, tools, and data. + +These components serve as the foundation for Microsoft Agent Framework. + +For more information, see [Microsoft Agent Framework](/agent-framework/overview/agent-framework-overview). diff --git a/docs/ai/index.yml b/docs/ai/index.yml index 1bc002a397c97..4a4879a321bb8 100644 --- a/docs/ai/index.yml +++ b/docs/ai/index.yml @@ -27,6 +27,8 @@ landingContent: url: overview.md - text: Microsoft.Extensions.AI libraries url: microsoft-extensions-ai.md + - text: Microsoft Agent Framework + url: /agent-framework/overview/agent-framework-overview?toc=/dotnet/ai/toc.json&bc=/dotnet/ai/toc.json - linkListType: get-started links: - text: Connect to and prompt an AI model diff --git a/docs/ai/media/agents/agent-components.png b/docs/ai/media/agents/agent-components.png new file mode 100644 index 0000000000000..bcc165855a993 Binary files /dev/null and b/docs/ai/media/agents/agent-components.png differ diff --git a/docs/ai/media/agents/concurrent-workflow.png b/docs/ai/media/agents/concurrent-workflow.png new file mode 100644 index 0000000000000..15e462c1b1e1d Binary files /dev/null and b/docs/ai/media/agents/concurrent-workflow.png differ diff --git a/docs/ai/media/agents/groupchat-workflow.png b/docs/ai/media/agents/groupchat-workflow.png new file mode 100644 index 0000000000000..7688d96539a69 Binary files /dev/null and b/docs/ai/media/agents/groupchat-workflow.png differ diff --git a/docs/ai/media/agents/handoff-workflow.png b/docs/ai/media/agents/handoff-workflow.png new file mode 100644 index 0000000000000..1cc58f22ec8f8 Binary files /dev/null and b/docs/ai/media/agents/handoff-workflow.png differ diff --git a/docs/ai/media/agents/sequential-workflow.png b/docs/ai/media/agents/sequential-workflow.png new file mode 100644 index 0000000000000..84f9a745bae53 Binary files /dev/null and b/docs/ai/media/agents/sequential-workflow.png differ diff --git a/docs/ai/toc.yml b/docs/ai/toc.yml index c53a1e1b86223..7d78494822e70 100644 --- a/docs/ai/toc.yml +++ b/docs/ai/toc.yml @@ -10,9 +10,9 @@ items: - name: Overview href: dotnet-ai-ecosystem.md - name: Microsoft.Extensions.AI - href: microsoft-extensions-ai.md - - name: Semantic Kernel - href: semantic-kernel-dotnet-overview.md + href: microsoft-extensions-ai.md + - name: Microsoft Agent Framework + href: /agent-framework/overview/agent-framework-overview?toc=/dotnet/ai/toc.json&bc=/dotnet/ai/toc.json - name: C# SDK for MCP href: get-started-mcp.md - name: Quickstarts @@ -41,6 +41,8 @@ items: items: - name: How generative AI and LLMs work href: conceptual/how-genai-and-llms-work.md + - name: Building agents in .NET + href: conceptual/agents.md - name: Tokens href: conceptual/understanding-tokens.md - name: Embeddings diff --git a/docs/azure/includes/dotnet-all.md b/docs/azure/includes/dotnet-all.md index b5a0af388e7b9..5f6f767810260 100644 --- a/docs/azure/includes/dotnet-all.md +++ b/docs/azure/includes/dotnet-all.md @@ -18,7 +18,7 @@ | Communication Email | NuGet [1.0.2](https://www.nuget.org/packages/Azure.Communication.Email/1.0.2)
NuGet [1.1.0-beta.2](https://www.nuget.org/packages/Azure.Communication.Email/1.1.0-beta.2) | [docs](/dotnet/api/overview/azure/Communication.Email-readme) | GitHub [1.0.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Email_1.0.2/sdk/communication/Azure.Communication.Email/)
GitHub [1.1.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Email_1.1.0-beta.2/sdk/communication/Azure.Communication.Email/) | | Communication Identity | NuGet [1.3.1](https://www.nuget.org/packages/Azure.Communication.Identity/1.3.1)
NuGet [1.4.0-beta.1](https://www.nuget.org/packages/Azure.Communication.Identity/1.4.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.Identity-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Identity_1.3.1/sdk/communication/Azure.Communication.Identity/)
GitHub [1.4.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Identity_1.4.0-beta.1/sdk/communication/Azure.Communication.Identity/) | | Communication JobRouter | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Communication.JobRouter/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.Communication.JobRouter/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.JobRouter-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.JobRouter_1.0.0/sdk/communication/Azure.Communication.JobRouter/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.JobRouter_1.1.0-beta.1/sdk/communication/Azure.Communication.JobRouter/) | -| Communication Messages | NuGet [1.1.0](https://www.nuget.org/packages/Azure.Communication.Messages/1.1.0)
NuGet [1.3.0-beta.1](https://www.nuget.org/packages/Azure.Communication.Messages/1.3.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.Messages-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.1.0/sdk/communication/Azure.Communication.Messages/)
GitHub [1.3.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.3.0-beta.1/sdk/communication/Azure.Communication.Messages/) | +| Communication Messages | NuGet [1.1.0](https://www.nuget.org/packages/Azure.Communication.Messages/1.1.0)
NuGet [1.3.0-beta.2](https://www.nuget.org/packages/Azure.Communication.Messages/1.3.0-beta.2) | [docs](/dotnet/api/overview/azure/Communication.Messages-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.1.0/sdk/communication/Azure.Communication.Messages/)
GitHub [1.3.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.3.0-beta.2/sdk/communication/Azure.Communication.Messages/) | | Communication Phone Numbers | NuGet [1.5.0](https://www.nuget.org/packages/Azure.Communication.PhoneNumbers/1.5.0) | [docs](/dotnet/api/overview/azure/Communication.PhoneNumbers-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.PhoneNumbers_1.5.0/sdk/communication/Azure.Communication.PhoneNumbers/) | | Communication Rooms | NuGet [1.2.0](https://www.nuget.org/packages/Azure.Communication.Rooms/1.2.0) | [docs](/dotnet/api/overview/azure/Communication.Rooms-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Rooms_1.2.0/sdk/communication/Azure.Communication.Rooms/) | | Communication SMS | NuGet [1.0.2](https://www.nuget.org/packages/Azure.Communication.Sms/1.0.2)
NuGet [1.1.0-beta.3](https://www.nuget.org/packages/Azure.Communication.Sms/1.1.0-beta.3) | [docs](/dotnet/api/overview/azure/Communication.Sms-readme) | GitHub [1.0.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Sms_1.0.2/sdk/communication/Azure.Communication.Sms/)
GitHub [1.1.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Sms_1.1.0-beta.3/sdk/communication/Azure.Communication.Sms/) | @@ -116,7 +116,7 @@ | Text Translation | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.Translation.Text/1.0.0) | [docs](/dotnet/api/overview/azure/AI.Translation.Text-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.Translation.Text_1.0.0/sdk/translation/Azure.AI.Translation.Text/) | | Time Series Insights | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.IoT.TimeSeriesInsights/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/IoT.TimeSeriesInsights-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.IoT.TimeSeriesInsights_1.0.0-beta.1/sdk/timeseriesinsights/Azure.IoT.TimeSeriesInsights/) | | TimeZone | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Maps.TimeZones/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Maps.TimeZones-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Maps.TimeZones_1.0.0-beta.1/sdk/maps/Azure.Maps.TimeZones/) | -| unknown | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.AI.VoiceLive/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/AI.VoiceLive-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.VoiceLive_1.0.0-beta.4/sdk/ai/Azure.AI.VoiceLive/) | +| unknown | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.VoiceLive/1.0.0) | [docs](/dotnet/api/overview/azure/AI.VoiceLive-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.VoiceLive_1.0.0/sdk/ai/Azure.AI.VoiceLive/) | | unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Analytics.OnlineExperimentation/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Analytics.OnlineExperimentation-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Analytics.OnlineExperimentation_1.0.0-beta.1/sdk/onlineexperimentation/Azure.Analytics.OnlineExperimentation/) | | unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Projects/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Projects-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Projects_1.0.0-beta.1/sdk/cloudmachine/Azure.Projects/) | | unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Projects.AI/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Projects.AI-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Projects.AI_1.0.0-beta.1/sdk/cloudmachine/Azure.Projects.AI/) | @@ -419,13 +419,13 @@ | Speech Extension Telemetry | NuGet [1.46.0](https://www.nuget.org/packages/Microsoft.CognitiveServices.Speech.Extension.Telemetry/1.46.0) | | | | System Net Client Model | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/System.Net.ClientModel/1.0.0-beta.1) | | | | Unknown Display Name | NuGet [0.13.0-preview](https://www.nuget.org/packages/Azure.Iot.Operations.Connector/0.13.0-preview) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp/0.8.3) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp.linux-arm64/0.8.3) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp.linux-x64/0.8.3) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp.osx-arm64/0.8.3) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp.osx-x64/0.8.3) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp.win-arm64/0.8.3) | | | -| Unknown Display Name | NuGet [0.8.3](https://www.nuget.org/packages/Azure.Mcp.win-x64/0.8.3) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp/0.8.4) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp.linux-arm64/0.8.4) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp.linux-x64/0.8.4) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp.osx-arm64/0.8.4) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp.osx-x64/0.8.4) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp.win-arm64/0.8.4) | | | +| Unknown Display Name | NuGet [0.8.4](https://www.nuget.org/packages/Azure.Mcp.win-x64/0.8.4) | | | | Unknown Display Name | NuGet [0.1.3-preview.2](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Aot/0.1.3-preview.2) | | | | Unknown Display Name | NuGet [0.2.802](https://www.nuget.org/packages/Microsoft.Azure.Mcp.AzTypes.Internal.Compact/0.2.802) | | | | Unknown Display Name | NuGet [1.1.2-preview](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.AzureCosmosDb.Mongo/1.1.2-preview) | | | diff --git a/docs/azure/includes/dotnet-new.md b/docs/azure/includes/dotnet-new.md index bb4655f1f0f5b..81454b75aef05 100644 --- a/docs/azure/includes/dotnet-new.md +++ b/docs/azure/includes/dotnet-new.md @@ -19,7 +19,7 @@ | Communication Email | NuGet [1.0.2](https://www.nuget.org/packages/Azure.Communication.Email/1.0.2)
NuGet [1.1.0-beta.2](https://www.nuget.org/packages/Azure.Communication.Email/1.1.0-beta.2) | [docs](/dotnet/api/overview/azure/Communication.Email-readme) | GitHub [1.0.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Email_1.0.2/sdk/communication/Azure.Communication.Email/)
GitHub [1.1.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Email_1.1.0-beta.2/sdk/communication/Azure.Communication.Email/) | | Communication Identity | NuGet [1.3.1](https://www.nuget.org/packages/Azure.Communication.Identity/1.3.1)
NuGet [1.4.0-beta.1](https://www.nuget.org/packages/Azure.Communication.Identity/1.4.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.Identity-readme) | GitHub [1.3.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Identity_1.3.1/sdk/communication/Azure.Communication.Identity/)
GitHub [1.4.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Identity_1.4.0-beta.1/sdk/communication/Azure.Communication.Identity/) | | Communication JobRouter | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Communication.JobRouter/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.Communication.JobRouter/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.JobRouter-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.JobRouter_1.0.0/sdk/communication/Azure.Communication.JobRouter/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.JobRouter_1.1.0-beta.1/sdk/communication/Azure.Communication.JobRouter/) | -| Communication Messages | NuGet [1.1.0](https://www.nuget.org/packages/Azure.Communication.Messages/1.1.0)
NuGet [1.3.0-beta.1](https://www.nuget.org/packages/Azure.Communication.Messages/1.3.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.Messages-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.1.0/sdk/communication/Azure.Communication.Messages/)
GitHub [1.3.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.3.0-beta.1/sdk/communication/Azure.Communication.Messages/) | +| Communication Messages | NuGet [1.1.0](https://www.nuget.org/packages/Azure.Communication.Messages/1.1.0)
NuGet [1.3.0-beta.2](https://www.nuget.org/packages/Azure.Communication.Messages/1.3.0-beta.2) | [docs](/dotnet/api/overview/azure/Communication.Messages-readme) | GitHub [1.1.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.1.0/sdk/communication/Azure.Communication.Messages/)
GitHub [1.3.0-beta.2](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Messages_1.3.0-beta.2/sdk/communication/Azure.Communication.Messages/) | | Communication Network Traversal | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Communication.NetworkTraversal/1.0.0)
NuGet [1.1.0-beta.1](https://www.nuget.org/packages/Azure.Communication.NetworkTraversal/1.1.0-beta.1) | [docs](/dotnet/api/overview/azure/Communication.NetworkTraversal-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.NetworkTraversal_1.0.0/sdk/communication/Azure.Communication.NetworkTraversal/)
GitHub [1.1.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.NetworkTraversal_1.1.0-beta.1/sdk/communication/Azure.Communication.NetworkTraversal/) | | Communication Phone Numbers | NuGet [1.5.0](https://www.nuget.org/packages/Azure.Communication.PhoneNumbers/1.5.0) | [docs](/dotnet/api/overview/azure/Communication.PhoneNumbers-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.PhoneNumbers_1.5.0/sdk/communication/Azure.Communication.PhoneNumbers/) | | Communication Rooms | NuGet [1.2.0](https://www.nuget.org/packages/Azure.Communication.Rooms/1.2.0) | [docs](/dotnet/api/overview/azure/Communication.Rooms-readme) | GitHub [1.2.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.Rooms_1.2.0/sdk/communication/Azure.Communication.Rooms/) | @@ -123,7 +123,7 @@ | Text Translation | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.Translation.Text/1.0.0) | [docs](/dotnet/api/overview/azure/AI.Translation.Text-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.Translation.Text_1.0.0/sdk/translation/Azure.AI.Translation.Text/) | | Time Series Insights | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.IoT.TimeSeriesInsights/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/IoT.TimeSeriesInsights-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.IoT.TimeSeriesInsights_1.0.0-beta.1/sdk/timeseriesinsights/Azure.IoT.TimeSeriesInsights/) | | TimeZone | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Maps.TimeZones/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Maps.TimeZones-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Maps.TimeZones_1.0.0-beta.1/sdk/maps/Azure.Maps.TimeZones/) | -| unknown | NuGet [1.0.0-beta.4](https://www.nuget.org/packages/Azure.AI.VoiceLive/1.0.0-beta.4) | [docs](/dotnet/api/overview/azure/AI.VoiceLive-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.4](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.VoiceLive_1.0.0-beta.4/sdk/ai/Azure.AI.VoiceLive/) | +| unknown | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.VoiceLive/1.0.0) | [docs](/dotnet/api/overview/azure/AI.VoiceLive-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.VoiceLive_1.0.0/sdk/ai/Azure.AI.VoiceLive/) | | unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Analytics.OnlineExperimentation/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Analytics.OnlineExperimentation-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Analytics.OnlineExperimentation_1.0.0-beta.1/sdk/onlineexperimentation/Azure.Analytics.OnlineExperimentation/) | | unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Projects/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Projects-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Projects_1.0.0-beta.1/sdk/cloudmachine/Azure.Projects/) | | unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Projects.AI/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/Projects.AI-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Projects.AI_1.0.0-beta.1/sdk/cloudmachine/Azure.Projects.AI/) | diff --git a/docs/core/testing/mstest-analyzers/mstest0051.md b/docs/core/testing/mstest-analyzers/mstest0051.md new file mode 100644 index 0000000000000..08c15940a4c52 --- /dev/null +++ b/docs/core/testing/mstest-analyzers/mstest0051.md @@ -0,0 +1,69 @@ +--- +title: "MSTEST0051: Assert.Throws should contain only a single statement" +description: "Learn about code analysis rule MSTEST0051: Assert.Throws should contain only a single statement" +ms.date: 10/01/2025 +f1_keywords: +- MSTEST0051 +- AssertThrowsShouldContainSingleStatementAnalyzer +helpviewer_keywords: +- AssertThrowsShouldContainSingleStatementAnalyzer +- MSTEST0051 +author: Youssef1313 +ms.author: ygerges +ai-usage: ai-generated +--- +# MSTEST0051: Assert.Throws should contain only a single statement + +| Property | Value | +|-------------------------------------|------------------------------------------------------------------------------------------| +| **Rule ID** | MSTEST0051 | +| **Title** | Assert.Throws should contain only a single statement | +| **Category** | Usage | +| **Fix is breaking or non-breaking** | Non-breaking | +| **Enabled by default** | Yes | +| **Default severity** | Info | +| **Introduced in version** | 3.11.0 | +| **Is there a code fix** | No | + +## Cause + +A call to , , , or contains multiple statements in the action delegate. + +## Rule description + +To ensure that only the specific method call that is expected to throw an exception is tested, the action delegate passed to `Assert.Throws`, `Assert.ThrowsAsync`, `Assert.ThrowsExactly`, or `Assert.ThrowsExactlyAsync` should contain exactly one statement. Including multiple statements can lead to passing tests when the exception is thrown by a statement that is not intended to throw. If it's not the last statement in the action that is intended to throw, then the test has dead code. If it's the last statement, the intent should be clearly stated. + +## How to fix violations + +Refactor the test to ensure that the action delegate contains only the single statement that is expected to throw the exception. Move any setup code outside the assertion call. + +For example, change this: + +```csharp +[TestMethod] +public void TestMethod() +{ + var obj = new MyClass(); + Assert.ThrowsExactly(() => + { + obj.Initialize(); + obj.Execute(); // Only this should be inside the assertion + }); +} +``` + +To this: + +```csharp +[TestMethod] +public void TestMethod() +{ + var obj = new MyClass(); + obj.Initialize(); + Assert.ThrowsExactly(() => obj.Execute()); +} +``` + +## When to suppress warnings + +Don't suppress warnings from this rule. Including multiple statements in the action delegate can make it unclear which operation is being tested and can lead to passing tests when the original intent is violated. diff --git a/docs/core/testing/mstest-analyzers/mstest0052.md b/docs/core/testing/mstest-analyzers/mstest0052.md new file mode 100644 index 0000000000000..6031b203fc76f --- /dev/null +++ b/docs/core/testing/mstest-analyzers/mstest0052.md @@ -0,0 +1,76 @@ +--- +title: "MSTEST0052: Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior" +description: "Learn about code analysis rule MSTEST0052: Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior" +ms.date: 10/01/2025 +f1_keywords: +- MSTEST0052 +- AvoidExplicitDynamicDataSourceTypeAnalyzer +helpviewer_keywords: +- AvoidExplicitDynamicDataSourceTypeAnalyzer +- MSTEST0052 +author: Youssef1313 +ms.author: ygerges +ai-usage: ai-generated +--- +# MSTEST0052: Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior + +| Property | Value | +|-------------------------------------|--------------------------------------------------------------------------------------------| +| **Rule ID** | MSTEST0052 | +| **Title** | Avoid passing an explicit 'DynamicDataSourceType' and use the default auto detect behavior | +| **Category** | Usage | +| **Fix is breaking or non-breaking** | Non-breaking | +| **Enabled by default** | Yes | +| **Default severity** | Warning | +| **Introduced in version** | 3.11.0 | +| **Is there a code fix** | Yes | + +## Cause + +A explicitly specifies or instead of using the default . + +## Rule description + +Starting with MSTest 3.8, `DynamicDataSourceType.AutoDetect` is the default value for the `DynamicDataSourceType` parameter in the `DynamicDataAttribute` constructor. This enhancement automatically detects whether the data source is a property, method, or field, eliminating the need to explicitly specify the source type. Using `AutoDetect` makes the code more maintainable and reduces verbosity. + +## How to fix violations + +Remove the explicit `DynamicDataSourceType` parameter from the `DynamicData` attribute and let the framework auto-detect the source type. + +For example, change this: + +```csharp +public static IEnumerable TestData { get; } = new[] +{ + new object[] { 1, 2, 3 }, + new object[] { 4, 5, 9 } +}; + +[TestMethod] +[DynamicData(nameof(TestData), DynamicDataSourceType.Property)] +public void TestMethod(int a, int b, int expected) +{ + Assert.AreEqual(expected, a + b); +} +``` + +To this: + +```csharp +public static IEnumerable TestData { get; } = new[] +{ + new object[] { 1, 2, 3 }, + new object[] { 4, 5, 9 } +}; + +[TestMethod] +[DynamicData(nameof(TestData))] +public void TestMethod(int a, int b, int expected) +{ + Assert.AreEqual(expected, a + b); +} +``` + +## When to suppress warnings + +Don't suppress warnings from this rule. Following the analyzer suggestion leads to less noise in the test code. diff --git a/docs/core/testing/mstest-analyzers/mstest0053.md b/docs/core/testing/mstest-analyzers/mstest0053.md new file mode 100644 index 0000000000000..b44274a561549 --- /dev/null +++ b/docs/core/testing/mstest-analyzers/mstest0053.md @@ -0,0 +1,66 @@ +--- +title: "MSTEST0053: Avoid using Assert methods with format parameters" +description: "Learn about code analysis rule MSTEST0053: Avoid using Assert methods with format parameters" +ms.date: 10/01/2025 +f1_keywords: +- MSTEST0053 +- AvoidAssertFormatParametersAnalyzer +helpviewer_keywords: +- AvoidAssertFormatParametersAnalyzer +- MSTEST0053 +author: Youssef1313 +ms.author: ygerges +ai-usage: ai-generated +--- +# MSTEST0053: Avoid using Assert methods with format parameters + +| Property | Value | +|-------------------------------------|------------------------------------------------------------------------------------------| +| **Rule ID** | MSTEST0053 | +| **Title** | Avoid using Assert methods with format parameters | +| **Category** | Usage | +| **Fix is breaking or non-breaking** | Non-breaking | +| **Enabled by default** | Yes | +| **Default severity** | Warning | +| **Introduced in version** | 3.11.0 | +| **Is there a code fix** | Yes | + +## Cause + +An assertion method call uses the `message` and `parameters` arguments for string formatting instead of using string interpolation. + +## Rule description + +Using the assertion overloads that accept `message` and `parameters` are no longer recommended. These overloads are removed in MSTest v4. It's advised to use string interpolation instead. + +## How to fix violations + +Replace calls that use message format parameters with string interpolation. + +For example, change this: + +```csharp +[TestMethod] +public void TestMethod() +{ + int expected = 5; + int actual = GetValue(); + Assert.AreEqual(expected, actual, "Expected {0} but got {1}", expected, actual); +} +``` + +To this: + +```csharp +[TestMethod] +public void TestMethod() +{ + int expected = 5; + int actual = GetValue(); + Assert.AreEqual(expected, actual, $"Expected {expected} but got {actual}"); +} +``` + +## When to suppress warnings + +Don't suppress warnings from this rule. These overloads are removed in MSTest v4 and are not recommended. diff --git a/docs/core/testing/mstest-analyzers/mstest0054.md b/docs/core/testing/mstest-analyzers/mstest0054.md new file mode 100644 index 0000000000000..d36f9effad832 --- /dev/null +++ b/docs/core/testing/mstest-analyzers/mstest0054.md @@ -0,0 +1,66 @@ +--- +title: "MSTEST0054: Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token" +description: "Learn about code analysis rule MSTEST0054: Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token" +ms.date: 10/01/2025 +f1_keywords: +- MSTEST0054 +- UseCancellationTokenPropertyAnalyzer +helpviewer_keywords: +- UseCancellationTokenPropertyAnalyzer +- MSTEST0054 +author: Youssef1313 +ms.author: ygerges +ai-usage: ai-generated +--- +# MSTEST0054: Use cancellation token from TestContext.CancellationToken + +| Property | Value | +|-------------------------------------|------------------------------------------------------------------------------------------| +| **Rule ID** | MSTEST0054 | +| **Title** | Use TestContext.CancellationToken instead of TestContext.CancellationTokenSource.Token | +| **Category** | Usage | +| **Fix is breaking or non-breaking** | Non-breaking | +| **Enabled by default** | Yes | +| **Default severity** | Info | +| **Introduced in version** | 3.11.0 | +| **Is there a code fix** | Yes | + +## Cause + +Accessing `CancellationToken` via `TestContext.CancellationTokenSource.Token` instead of using the `TestContext.CancellationToken` property. + +## Rule description + +MSTest provides a cancellation token through the `TestContext.CancellationToken` property. Accessing `TestContext.CancellationTokenSource` is not recommended, and it might be removed in a future release. It's also simpler to use `TestContext.CancellationToken` compared to `TestContext.CancellationTokenSource.Token`. + +## How to fix violations + +Use the `TestContext.CancellationToken` property instead of `TestContext.CancellationTokenSource.Token`. + +For example, change this: + +```csharp +public TestContext TestContext { get; set; } + +[TestMethod] +public async Task TestMethod() +{ + await Task.Delay(1000, TestContext.CancellationTokenSource.Token); +} +``` + +To this: + +```csharp +public TestContext TestContext { get; set; } + +[TestMethod] +public async Task TestMethod() +{ + await Task.Delay(1000, TestContext.CancellationToken); +} +``` + +## When to suppress warnings + +Don't suppress warnings from this rule. The use of `CancellationTokenSource` property is not recommended and might be removed in a future release. diff --git a/docs/core/testing/mstest-analyzers/mstest0055.md b/docs/core/testing/mstest-analyzers/mstest0055.md new file mode 100644 index 0000000000000..209244376392f --- /dev/null +++ b/docs/core/testing/mstest-analyzers/mstest0055.md @@ -0,0 +1,64 @@ +--- +title: "MSTEST0055: Do not ignore the return value of string methods" +description: "Learn about code analysis rule MSTEST0055: Do not ignore the return value of string methods" +ms.date: 10/01/2025 +f1_keywords: +- MSTEST0055 +- IgnoreStringMethodReturnValueAnalyzer +helpviewer_keywords: +- IgnoreStringMethodReturnValueAnalyzer +- MSTEST0055 +author: Youssef1313 +ms.author: ygerges +ai-usage: ai-generated +--- +# MSTEST0055: Do not ignore the return value of string methods + +| Property | Value | +|-------------------------------------|------------------------------------------------------------------------------------------| +| **Rule ID** | MSTEST0055 | +| **Title** | Do not ignore the return value of string methods | +| **Category** | Usage | +| **Fix is breaking or non-breaking** | Non-breaking | +| **Enabled by default** | Yes | +| **Default severity** | Warning | +| **Introduced in version** | 3.11.0 | +| **Is there a code fix** | No | + +## Cause + +A call to `string.Contains`, `string.StartsWith`, or `string.EndsWith` is made and its return value is ignored. + +## Rule description + +Those methods don't have any side effects and ignoring the return result is always wrong. It's more likely that the original intent of those calls are to assert that they are true. + +## How to fix violations + +Capture and use the return value from string methods, or use a proper assertion method. + +For example, change this: + +```csharp +[TestMethod] +public void TestMethod() +{ + string value = "Hello world"; + value.StartsWith("Hello"); +} +``` + +To this: + +```csharp +[TestMethod] +public void TestMethod() +{ + string value = "Hello world"; + Assert.IsTrue(value.StartsWith("Hello")); // or, Assert.StartsWith("Hello", value); +} +``` + +## When to suppress warnings + +Don't suppress warnings from this rule. Calling string methods without using their return value is always a bug or a dead code. diff --git a/docs/core/testing/mstest-analyzers/usage-rules.md b/docs/core/testing/mstest-analyzers/usage-rules.md index 081ae40c21f5e..50c3bc7503de9 100644 --- a/docs/core/testing/mstest-analyzers/usage-rules.md +++ b/docs/core/testing/mstest-analyzers/usage-rules.md @@ -3,7 +3,7 @@ title: MSTest Usage rules (code analysis) description: Learn about MSTest code analysis usage rules. author: evangelink ms.author: amauryleve -ms.date: 01/03/2024 +ms.date: 10/01/2025 --- # MSTest usage rules @@ -44,3 +44,8 @@ Identifier | Name | Description [MSTEST0048](mstest0048.md) | TestContextPropertyUsageAnalyzer | A fixture method (methods with , , , or ) accesses restricted properties. [MSTEST0049](mstest0049.md) | FlowTestContextCancellationTokenAnalyzer | A method call within a test context doesn't use the available from when the called method has a parameter or overload that accepts a . [MSTEST0050](mstest0050.md) | GlobalTestFixtureShouldBeValidAnalyzer | A global test fixture method (marked with `GlobalTestInitializeAttribute` or `GlobalTestCleanupAttribute`) doesn't follow the required layout or has invalid configuration. +[MSTEST0051](mstest0051.md) | AssertThrowsShouldContainSingleStatementAnalyzer | A call to , , , or contains multiple statements in the action delegate. +[MSTEST0052](mstest0052.md) | AvoidExplicitDynamicDataSourceTypeAnalyzer | A explicitly specifies or instead of using the default . +[MSTEST0053](mstest0053.md) | AvoidAssertFormatParametersAnalyzer | An assertion method call uses the `message` and `parameters` arguments for string formatting instead of using string interpolation. +[MSTEST0054](mstest0054.md) | UseCancellationTokenPropertyAnalyzer | Accessing `CancellationToken` via `TestContext.CancellationTokenSource.Token` instead of using the `TestContext.CancellationToken` property. +[MSTEST0055](mstest0055.md) | IgnoreStringMethodReturnValueAnalyzer | A call to `string.Contains`, `string.StartsWith`, or `string.EndsWith` is made and its return value is ignored. diff --git a/docs/core/testing/unit-testing-with-dotnet-test.md b/docs/core/testing/unit-testing-with-dotnet-test.md index 473c6a13363c0..df5a680a908a3 100644 --- a/docs/core/testing/unit-testing-with-dotnet-test.md +++ b/docs/core/testing/unit-testing-with-dotnet-test.md @@ -26,7 +26,7 @@ The process involves invoking the `VSTest` MSBuild target, which triggers other ### Run MTP projects with VSTest mode -`dotnet test` was designed to run VSTest projects in VSTest mode. However, you can run MTP projects in `dotnet test` VSTest mode by using the [Microsoft.Testing.Platform.MSBuild](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild) package. From the user's perspective, this support is enabled by setting the `TestingPlatformDotnetTestSupport` MSBuild property to `true` (it's `false` by default for backward-compatibility reasons). When this property is set to `true`, Microsoft.Testing.Platform.MSBuild changes the `VSTest` target behavior, redirecting it to call `InvokeTestingPlatform`. `InvokeTestingPlatform` is an MSBuild target included in Microsoft.Testing.Platform.MSBuild that's responsible for correctly running MTP test applications as executables. VSTest-specific command-line options, such as `--logger`, are silently ignored in this mode. To include MTP-specific arguments, such as `--report-trx`, you must append them after an additional `--`. For example, `dotnet test -- --report-trx`. +`dotnet test` was designed to run VSTest projects in VSTest mode. However, you can run MTP projects in `dotnet test` VSTest mode by using the [Microsoft.Testing.Platform.MSBuild](https://www.nuget.org/packages/Microsoft.Testing.Platform.MSBuild) package. From the user's perspective, this support is enabled by setting the `TestingPlatformDotnetTestSupport` MSBuild property to `true` (it's `false` by default for backward-compatibility reasons). When this property is set to `true`, Microsoft.Testing.Platform.MSBuild changes the `VSTest` target behavior, redirecting it to call `InvokeTestingPlatform`. `InvokeTestingPlatform` is an MSBuild target included in Microsoft.Testing.Platform.MSBuild that's responsible for correctly running MTP test applications as executables. VSTest-specific command-line options, such as `--logger`, are silently ignored in this mode. To include MTP-specific arguments, such as `--report-trx`, you must append them after an additional `--`. For example, `dotnet test -- --report-trx`. In MTP 1.9, a warning with code MTP0001 is produced when an argument that is silently ignored is detected. > [!NOTE] > MSTest and NUnit use the [Microsoft.Testing.Extensions.VSTestBridge](https://www.nuget.org/packages/Microsoft.Testing.Extensions.VSTestBridge) package. By setting `EnableMSTestRunner` or `EnableNUnitRunner` (which enables Microsoft.Testing.Platform), your test project will support both VSTest and Microsoft.Testing.Platform. @@ -36,7 +36,7 @@ The process involves invoking the `VSTest` MSBuild target, which triggers other > It is highly recommended to set the `TestingPlatformDotnetTestSupport` property in `Directory.Build.props`. This ensures that you don't need to add it to every test project file individually. Additionally, it prevents the risk of introducing a new test project that doesn't set this property, which could result in a solution where some projects use VSTest while others use Microsoft.Testing.Platform. This mixed configuration might not work correctly and is an unsupported scenario. > > [!IMPORTANT] -> Running MTP projects under VSTest mode is considered legacy in favor of the newer experience in .NET 10 SDK. The support of running under this mode will be removed in Microsoft.Testing.Platform version 2. +> Running MTP projects under VSTest mode is considered legacy in favor of the newer experience in .NET 10 SDK. The support of running under this mode will be removed in Microsoft.Testing.Platform version 2 if run with .NET 10 SDK. The support remains available for .NET 9 SDK and earlier for backward compatibility. > For more information, see [Migrate to MTP mode of `dotnet test`](#migrate-to-mtp-mode-of-dotnet-test). The following list outlines the command-line options of `dotnet test` command in VSTest mode that are supported by Microsoft.Testing.Platform. These options are specific to the build process and not passed down to VSTest, which is why they work with MTP. diff --git a/docs/core/tools/dotnet-package-update.md b/docs/core/tools/dotnet-package-update.md new file mode 100644 index 0000000000000..53007081f211d --- /dev/null +++ b/docs/core/tools/dotnet-package-update.md @@ -0,0 +1,117 @@ +--- +title: dotnet package update command +description: Update PackageReferences in a project. +author: zivkan +ms.date: 10/03/2025 +--- +# dotnet package update + +**This article applies to:** ✔️ .NET 10 SDK and later versions + +## Name + +`dotnet package update` - Update referenced packages in a project. + +## Synopsis + +```dotnetcli +dotnet package update [...] + [--interactive] [--project ] + [--verbosity ] [--vulnerable] + +dotnet package update -h|--help +``` + +## Description + +The `dotnet package update` command updates packages used by projects. +If [NuGetAudit](/nuget/concepts/auditing-packages) is enabled, it can also attempt to automatically update update packages with known vulnerabilities to fixed versions. + +### Warnings as Errors + +`dotnet package update` does implicit restores to check if the resulting package graph is free of errors. +Using `--vulnerable` also does an implicit restore to find NuGetAudit warnings. +However, if your project uses `WarningsAsErrors` or `TreatWarningsAsErrors`, NuGet's restore warnings can cause restore to fail, preventing the update from completing. + +We recommend taking advantage of MSBuild conditions and environment variables as a workaround until [this feature request](https://github.com/NuGet/Home/issues/14311) is implemented. +For example, set `](../casting-and-conversions.md#downcasting)) that will always succeed based on the types involved, making the operation unnecessary. + +Redundant Type test: + +[!code-fsharp[FS0067-redundant-type-test](~/samples/snippets/fsharp/compiler-messages/fs0067.fsx#L2-L8)] + +Redundant Downcast: + +[!code-fsharp[FS0067-redundant-downcast](~/samples/snippets/fsharp/compiler-messages/fs0067.fsx#L11-L18)] + +The two examples above cause the compiler to display the following message: + +```text +FS0067: This type test or downcast will always hold +``` + +The use of `:?` and `:?>` operators is preferable when working with: + +- Base classes when the runtime type might differ. +- Values of type `obj` or interfaces types. diff --git a/docs/fsharp/language-reference/compiler-messages/toc.yml b/docs/fsharp/language-reference/compiler-messages/toc.yml index bd3d78920cd34..9e1a0a553ecd1 100644 --- a/docs/fsharp/language-reference/compiler-messages/toc.yml +++ b/docs/fsharp/language-reference/compiler-messages/toc.yml @@ -19,5 +19,7 @@ href: ./fs0037.md - name: FS0052 - Defensive copy href: ./fs0052.md + - name: FS0067 - This type test or downcast will always hold + href: ./fs0067.md - name: FS0703 - Expected type parameter, not unit-of-measure parameter href: ./fs0703.md diff --git a/docs/fsharp/language-reference/discriminated-unions.md b/docs/fsharp/language-reference/discriminated-unions.md index 247ef28f82161..0741462746024 100644 --- a/docs/fsharp/language-reference/discriminated-unions.md +++ b/docs/fsharp/language-reference/discriminated-unions.md @@ -18,6 +18,12 @@ type [accessibility-modifier] type-name = [ member-list ] ``` +The first pipe character (`|`) is optional. For single-case discriminated unions, you can omit it: + +```fsharp +type SingleCase = Case of string +``` + ## Remarks Discriminated unions are similar to union types in other languages, but there are differences. As with a union type in C++ or a variant type in Visual Basic, the data stored in the value is not fixed; it can be one of several distinct options. Unlike unions in these other languages, however, each of the possible options is given a *case identifier*. The case identifiers are names for the various possible types of values that objects of this type could be; the values are optional. If values are not present, the case is equivalent to an enumeration case. If values are present, each value can either be a single value of a specified type, or a tuple that aggregates multiple fields of the same or different types. You can give an individual field a name, but the name is optional, even if other fields in the same case are named. diff --git a/docs/fsharp/language-reference/interpolated-strings.md b/docs/fsharp/language-reference/interpolated-strings.md index 26f9f1e68933c..e9aeed973853d 100644 --- a/docs/fsharp/language-reference/interpolated-strings.md +++ b/docs/fsharp/language-reference/interpolated-strings.md @@ -1,7 +1,7 @@ --- title: Interpolated strings description: Learn about interpolated strings, a special form of string that allows you to embed F# expressions directly inside them. -ms.date: 11/12/2020 +ms.date: 10/01/2025 --- # Interpolated strings @@ -13,6 +13,8 @@ Interpolated strings are [strings](strings.md) that allow you to embed F# expres ```fsharp $"string-text {expr}" $"string-text %format-specifier{expr}" +$@"string-text {expr}" +@$"string-text {expr}" $"""string-text {"embedded string literal"}""" $$"""string-text %%format-specifier{{expr}}""" ``` @@ -56,7 +58,29 @@ In the previous example, the code mistakenly passes the `age` value where `name` ## Verbatim interpolated strings -F# supports verbatim interpolated strings with triple quotes so that you can embed string literals. +F# supports verbatim interpolated strings in two ways: + +### Using `$@` or `@$` prefix + +You can combine the interpolation prefix `$` with the verbatim string prefix `@` in any order. Verbatim strings ignore escape sequences (except for `""` to represent a quotation mark) and can span multiple lines. This is especially useful when working with file paths or strings containing backslashes and quotes. + +```fsharp +let name = "Alice" +let path = @"C:\Users\Alice\Documents" + +// Using $@ prefix +printfn $@"User {name} has files in: {path}" + +// Using @$ prefix (also valid) +printfn @$"User {name} has files in: {path}" + +// Embedding quotes - use "" to represent a single " +let message = $@"He said ""{name}"" is here" +``` + +### Using triple quotes + +F# also supports verbatim interpolated strings with triple quotes so that you can embed string literals without escaping. ```fsharp let age = 30 diff --git a/docs/fsharp/language-reference/lazy-expressions.md b/docs/fsharp/language-reference/lazy-expressions.md index dcf138aec4262..261d3877cd3cb 100644 --- a/docs/fsharp/language-reference/lazy-expressions.md +++ b/docs/fsharp/language-reference/lazy-expressions.md @@ -1,7 +1,7 @@ --- title: Lazy Expressions description: Learn how F# lazy expressions can improve the performance of your apps and libraries. -ms.date: 08/15/2020 +ms.date: 10/02/2025 --- # Lazy Expressions @@ -27,6 +27,21 @@ The following code illustrates the use of lazy expressions and the use of `Force Lazy evaluation, but not the `Lazy` type, is also used for sequences. For more information, see [Sequences](sequences.md). +## Formatting + +For multiline lazy expressions, place the opening parenthesis on the same line as the `lazy` keyword, with the expression body indented one level: + +```fsharp +let expensiveCalculation = + lazy ( + let step1 = performStep1() + let step2 = performStep2 step1 + step2 * 2 + ) +``` + +For more information on formatting lazy expressions, see the [F# formatting guide](../style-guide/formatting.md#formatting-lazy-expressions). + ## See also - [F# Language Reference](index.md) diff --git a/docs/fsharp/language-reference/members/properties.md b/docs/fsharp/language-reference/members/properties.md index 33595f0b7ed6e..83cb0d058a6ba 100644 --- a/docs/fsharp/language-reference/members/properties.md +++ b/docs/fsharp/language-reference/members/properties.md @@ -14,18 +14,17 @@ ms.date: 05/16/2016 // Property that has both get and set defined. [ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName -with [accessibility-modifier] get() = +with [ attributes-for-get ] [accessibility-modifier] get() = get-function-body -and [accessibility-modifier] set parameter = +and [ attributes-for-set ] [accessibility-modifier] set parameter = set-function-body // Alternative syntax for a property that has get and set. -[ attributes-for-get ] -[ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName = +[ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName +with [ attributes-for-get ] get() = get-function-body -[ attributes-for-set ] [ static ] member [accessibility-modifier-for-set] [self-identifier.]PropertyName -with set parameter = +with [ attributes-for-set ] set parameter = set-function-body // Property that has get only. @@ -34,15 +33,13 @@ with set parameter = get-function-body // Alternative syntax for property that has get only. -[ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName -with get() = +with [ attributes ] get() = get-function-body // Property that has set only. -[ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName -with set parameter = +with [ attributes ] set parameter = set-function-body // Automatically implemented properties. diff --git a/docs/fsharp/language-reference/operator-overloading.md b/docs/fsharp/language-reference/operator-overloading.md index f16a1924c5945..c976ddb24f5fd 100644 --- a/docs/fsharp/language-reference/operator-overloading.md +++ b/docs/fsharp/language-reference/operator-overloading.md @@ -126,6 +126,9 @@ Other combinations of operator characters that are not listed here can be used a |`)`|`RParen`| |`[`|`LBrack`| |`]`|`RBrack`| +|`:`|`Colon`| + +Use of `:` in custom operators is partially reserved. It may only be used in operators where the first character is `>` or `.` where the first character after any number of leading `.` is `>` e.g. `>:` or `.>:`. ## Prefix and Infix Operators diff --git a/docs/fsharp/language-reference/pattern-matching.md b/docs/fsharp/language-reference/pattern-matching.md index 4ac2d0e7c6a87..b9d23162ffe36 100644 --- a/docs/fsharp/language-reference/pattern-matching.md +++ b/docs/fsharp/language-reference/pattern-matching.md @@ -174,6 +174,10 @@ The record pattern is used to decompose records to extract the values of fields. The wildcard pattern is represented by the underscore (`_`) character and matches any input, just like the variable pattern, except that the input is discarded instead of assigned to a variable. The wildcard pattern is often used within other patterns as a placeholder for values that are not needed in the expression to the right of the `->` symbol. The wildcard pattern is also frequently used at the end of a list of patterns to match any unmatched input. The wildcard pattern is demonstrated in many code examples in this topic. See the preceding code for one example. +The following code shows some additional uses of the wildcard pattern: + +[!code-fsharp[Main](~/samples/snippets/fsharp/lang-ref-2/snippet4818.fs)] + ## Patterns That Have Type Annotations Patterns can have type annotations. These behave like other type annotations and guide inference like other type annotations. Parentheses are required around type annotations in patterns. The following code shows a pattern that has a type annotation. diff --git a/docs/fsharp/language-reference/query-expressions.md b/docs/fsharp/language-reference/query-expressions.md index 4fe4f26d42f69..72b16c4f9b709 100644 --- a/docs/fsharp/language-reference/query-expressions.md +++ b/docs/fsharp/language-reference/query-expressions.md @@ -1,7 +1,8 @@ --- title: Query Expressions description: Learn about query expression support for LINQ in the F# programming language. -ms.date: 08/15/2020 +ms.date: 09/26/2025 +ai-usage: ai-assisted --- # Query expressions @@ -15,27 +16,9 @@ query { expression } ## Remarks -Query expressions are a type of computation expression similar to sequence expressions. Just as you specify a sequence by providing code in a sequence expression, you specify a set of data by providing code in a query expression. In a sequence expression, the `yield` keyword identifies data to be returned as part of the resulting sequence. In query expressions, the `select` keyword performs the same function. In addition to the `select` keyword, F# also supports a number of query operators that are much like the parts of a SQL SELECT statement. Here is an example of a simple query expression, along with code that connects to the Northwind OData source. +Query expressions are a type of computation expression similar to sequence expressions. Just as you specify a sequence by providing code in a sequence expression, you specify a set of data by providing code in a query expression. In a sequence expression, the `yield` keyword identifies data to be returned as part of the resulting sequence. In query expressions, the `select` keyword performs the same function. In addition to the `select` keyword, F# also supports a number of query operators that are much like the parts of a SQL SELECT statement. Here is an example of a simple query expression using Entity Framework Core to query a database. -```fsharp -// Use the OData type provider to create types that can be used to access the Northwind database. -// Add References to FSharp.Data.TypeProviders and System.Data.Services.Client -open Microsoft.FSharp.Data.TypeProviders - -type Northwind = ODataService<"http://services.odata.org/Northwind/Northwind.svc"> -let db = Northwind.GetDataContext() - -// A query expression. -let query1 = - query { - for customer in db.Customers do - select customer - } - -// Print results -query1 -|> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName) -``` +:::code language="fsharp" source="~/samples/snippets/fsharp/query-expressions/snippet1.fs"::: In the previous code example, the query expression is in curly braces. The meaning of the code in the expression is, return every customer in the Customers table in the database in the query results. Query expressions return a type that implements and , and so they can be iterated using the [Seq module](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-seqmodule.html) as the example shows. @@ -53,22 +36,9 @@ This table assumes a database in the following form: ![Diagram that shows a sample database.](./media/query-expressions/student-course-database.png) -The code in the tables that follow also assumes the following database connection code. Projects should add references to System.Data, System.Data.Linq, and FSharp.Data.TypeProviders assemblies. The code that creates this database is included at the end of this topic. - -```fsharp -open System -open Microsoft.FSharp.Data.TypeProviders -open System.Data.Linq.SqlClient -open System.Linq -open Microsoft.FSharp.Linq - -type schema = SqlDataConnection< @"Data Source=SERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;" > - -let db = schema.GetDataContext() +The code in the tables that follow also assumes the following database connection code using Entity Framework Core. Projects should add package references to `Microsoft.EntityFrameworkCore` and `Microsoft.EntityFrameworkCore.InMemory` (or another EF Core provider for production scenarios). This example uses the in-memory database provider for demonstration purposes, but the same query syntax works with any EF Core provider (SQL Server, PostgreSQL, etc.). -// Needed for some query operator examples: -let data = [ 1; 5; 7; 11; 18; 21] -``` +:::code language="fsharp" source="~/samples/snippets/fsharp/query-expressions/snippet2.fs"::: ### Table 1. Query Operators @@ -84,6 +54,7 @@ let data = [ 1; 5; 7; 11; 18; 21] ```fsharp query { for student in db.Student do + where student.Age.IsSome select student.Age.Value contains 11 } @@ -242,7 +213,7 @@ query { query { for student in db.Student do where student.Age.HasValue - sortBy student.Age.Value + sortByNullable student.Age thenBy student.Name select student } @@ -255,7 +226,7 @@ query { query { for student in db.Student do where student.Age.HasValue - sortBy student.Age.Value + sortByNullable student.Age thenByDescending student.Name select student } @@ -1350,19 +1321,71 @@ VALUES(15, 7, 3); The following code contains the sample code that appears in this topic. ```fsharp -#if INTERACTIVE -#r "FSharp.Data.TypeProviders.dll" -#r "System.Data.dll" -#r "System.Data.Linq.dll" -#endif open System -open Microsoft.FSharp.Data.TypeProviders -open System.Data.Linq.SqlClient open System.Linq -type schema = SqlDataConnection<"Data Source=SERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;"> - -let db = schema.GetDataContext() +// Define simple data types to represent our sample database +type Student = { + StudentID: int + Name: string + Age: int option +} + +type Course = { + CourseID: int + CourseName: string +} + +type CourseSelection = { + ID: int + StudentID: int + CourseID: int +} + +// Sample data +let students = [ + { StudentID = 1; Name = "Abercrombie, Kim"; Age = Some 10 } + { StudentID = 2; Name = "Abolrous, Hazen"; Age = Some 14 } + { StudentID = 3; Name = "Hance, Jim"; Age = Some 12 } + { StudentID = 4; Name = "Adams, Terry"; Age = Some 12 } + { StudentID = 5; Name = "Hansen, Claus"; Age = Some 11 } + { StudentID = 6; Name = "Penor, Lori"; Age = Some 13 } + { StudentID = 7; Name = "Perham, Tom"; Age = Some 12 } + { StudentID = 8; Name = "Peng, Yun-Feng"; Age = None } +] + +let courses = [ + { CourseID = 1; CourseName = "Algebra I" } + { CourseID = 2; CourseName = "Trigonometry" } + { CourseID = 3; CourseName = "Algebra II" } + { CourseID = 4; CourseName = "History" } + { CourseID = 5; CourseName = "English" } + { CourseID = 6; CourseName = "French" } + { CourseID = 7; CourseName = "Chinese" } +] + +let courseSelections = [ + { ID = 1; StudentID = 1; CourseID = 2 } + { ID = 2; StudentID = 1; CourseID = 3 } + { ID = 3; StudentID = 1; CourseID = 5 } + { ID = 4; StudentID = 2; CourseID = 2 } + { ID = 5; StudentID = 2; CourseID = 5 } + { ID = 6; StudentID = 2; CourseID = 6 } + { ID = 7; StudentID = 2; CourseID = 3 } + { ID = 8; StudentID = 3; CourseID = 2 } + { ID = 9; StudentID = 3; CourseID = 1 } + { ID = 10; StudentID = 4; CourseID = 2 } + { ID = 11; StudentID = 4; CourseID = 5 } + { ID = 12; StudentID = 4; CourseID = 2 } + { ID = 13; StudentID = 5; CourseID = 3 } + { ID = 14; StudentID = 5; CourseID = 2 } + { ID = 15; StudentID = 7; CourseID = 3 } +] + +// Convert to queryable collections for LINQ operations +let db.Student = students.AsQueryable() +let coursesQueryable = courses.AsQueryable() +let courseSelectionsQueryable = courseSelections.AsQueryable() let data = [1; 5; 7; 11; 18; 21] @@ -1984,15 +2007,7 @@ query { And here is the full output when this code is run in F# Interactive. ```console ---> Referenced 'C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\3.0\Runtime\v4.0\Type Providers\FSharp.Data.TypeProviders.dll' - ---> Referenced 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll' - ---> Referenced 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.Linq.dll' - contains query operator -Binding session to 'C:\Users\ghogen\AppData\Local\Temp\tmp5E3C.dll'... -Binding session to 'C:\Users\ghogen\AppData\Local\Temp\tmp611A.dll'... Is at least one student age 11? true count query operator diff --git a/docs/fsharp/language-reference/snippets/interpolated-strings/verbatim.fsx b/docs/fsharp/language-reference/snippets/interpolated-strings/verbatim.fsx new file mode 100644 index 0000000000000..52db7c8163ebb --- /dev/null +++ b/docs/fsharp/language-reference/snippets/interpolated-strings/verbatim.fsx @@ -0,0 +1,19 @@ +// Verbatim interpolated strings with $@ or @$ +let name = "Alice" +let path = @"C:\Users\Alice\Documents" + +// Using $@ prefix (interpolated verbatim string) +printfn $@"User {name} has files in: {path}" + +// Using @$ prefix (also valid) +printfn @$"User {name} has files in: {path}" + +// Embedding quotes without escaping +let message = $@"He said ""{name}"" is here" +printfn "%s" message + +// Multi-line verbatim interpolated strings +let multiline = $@"Name: {name} +Path: {path} +Status: Active" +printfn "%s" multiline diff --git a/docs/fsharp/style-guide/formatting.md b/docs/fsharp/style-guide/formatting.md index fb7fc61872bd9..927ba4411270d 100644 --- a/docs/fsharp/style-guide/formatting.md +++ b/docs/fsharp/style-guide/formatting.md @@ -1,7 +1,7 @@ --- title: F# code formatting guidelines description: Learn guidelines for formatting F# code. -ms.date: 11/01/2023 +ms.date: 10/02/2025 --- # F# code formatting guidelines @@ -539,6 +539,41 @@ let useAddEntry () = bar () ``` +### Formatting lazy expressions + +When writing single-line lazy expressions, keep everything on one line: + +```fsharp +// ✔️ OK +let x = lazy (computeValue()) + +// ✔️ OK +let y = lazy (a + b) +``` + +For multiline lazy expressions, place the opening parenthesis on the same line as the `lazy` keyword, with the expression body indented one level and the closing parenthesis aligned with the opening: + +```fsharp +// ✔️ OK +let v = + lazy ( + // some code + let x = computeExpensiveValue() + let y = computeAnotherValue() + x + y + ) + +// ✔️ OK +let handler = + lazy ( + let connection = openConnection() + let data = fetchData connection + processData data + ) +``` + +This follows the same pattern as other function applications with multiline arguments. The opening parenthesis stays with `lazy`, and the expression is indented one level. + ### Formatting arithmetic and binary expressions Always use white space around binary arithmetic expressions: diff --git a/docs/fsharp/using-fsharp-on-azure/blob-storage.md b/docs/fsharp/using-fsharp-on-azure/blob-storage.md index c5e34b64d3973..0cd16f8f5e855 100644 --- a/docs/fsharp/using-fsharp-on-azure/blob-storage.md +++ b/docs/fsharp/using-fsharp-on-azure/blob-storage.md @@ -2,7 +2,7 @@ title: Get started with Azure Blob Storage using F# description: Store unstructured data in the cloud with Azure Blob Storage. author: sylvanc -ms.date: 09/17/2024 +ms.date: 10/02/2025 ms.custom: "devx-track-fsharp" ai-usage: ai-assisted --- @@ -10,12 +10,15 @@ ai-usage: ai-assisted Azure Blob Storage is a service that stores unstructured data in the cloud as objects/blobs. Blob storage can store any type of text or binary data, such as a document, media file, or application installer. Blob storage is also referred to as object storage. -This article shows you how to perform common tasks using Blob storage. The samples are written using F# using the Azure Storage Client Library for .NET. The tasks covered include how to upload, list, download, and delete blobs. +This article shows you how to perform common tasks using Blob storage. The samples are written using F# with the `Azure.Storage.Blobs` package. The tasks covered include how to upload, list, download, and delete blobs. For a conceptual overview of blob storage, see [the .NET guide for blob storage](/azure/storage/blobs/storage-quickstart-blobs-dotnet). For ease, these tutorials use [connection strings](/azure/storage/storage-configure-connection-string) to authenticate with Azure. For production applications, you should use Microsoft Entra ID with [managed identities](/entra/identity/managed-identities-azure-resources/) or the [Azure.Identity library](/dotnet/api/overview/azure/identity-readme) for enhanced security. +> [!IMPORTANT] +> This article uses the modern `Azure.Storage.Blobs` package. If you're updating from older code that used the deprecated `WindowsAzure.Storage` or `Microsoft.Azure.Storage.Blob` packages, you need to update your package references and namespace declarations. For migration guidance, see [Migrate to Azure.Storage.Blobs](/azure/storage/blobs/storage-quickstart-blobs-dotnet). + > [!NOTE] -> This article uses the standard Azure Storage Client Library for .NET. F# developers might consider the [FSharp.Azure.Blob](https://github.com/random82/FSharp.Azure.Blob) library, which provides a more idiomatic F# API for working with Azure Blob Storage. This community library offers idiomatic F# functions and patterns that make blob operations more natural in F#. +> F# developers might consider the [FSharp.Azure.Blob](https://github.com/random82/FSharp.Azure.Blob) library, which provides a more idiomatic F# API for working with Azure Blob Storage. This community library offers idiomatic F# functions and patterns that make blob operations more natural in F#. ## Prerequisites @@ -35,7 +38,7 @@ F# Interactive, `dotnet fsi`, can be launched interactively, or it can be launch ### Add packages in a script -Next, use `#r` `nuget:package name` to install the `Azure.Storage.Blobs` package and `open` namespaces.Such as +Use `#r` `nuget:package name` to install the `Azure.Storage.Blobs` package and `open` the required namespaces: ```fsharp > #r "nuget: Azure.Storage.Blobs" diff --git a/docs/fundamentals/code-analysis/quality-rules/ca1045.md b/docs/fundamentals/code-analysis/quality-rules/ca1045.md index 6c3636aa9d513..c142af836bef6 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca1045.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca1045.md @@ -31,18 +31,18 @@ Passing types by reference (using `out` or `ref`) requires experience with point When a reference type is passed "by reference," the method intends to use the parameter to return a different instance of the object. (Passing a reference type by reference is also known as using a double pointer, pointer to a pointer, or double indirection.) Using the default calling convention, which is pass "by value," a parameter that takes a reference type already receives a pointer to the object. The pointer, not the object to which it points, is passed by value. Passing by value means that the method cannot change the pointer to have it point to a new instance of the reference type, but can change the contents of the object to which it points. For most applications this is sufficient and yields the behavior that you want. -If a method must return a different instance, use the return value of the method to accomplish this. See the class for a variety of methods that operate on strings and return a new instance of a string. By using this model, it is left to the caller to decide whether the original object is preserved. +If a method must return a different instance, use the return value of the method to accomplish this. For methods that operate on strings and return a new instance of a string, see the class. By using this model, it is left to the caller to decide whether the original object is preserved. Although return values are commonplace and heavily used, the correct application of `out` and `ref` parameters requires intermediate design and coding skills. Library architects who design for a general audience should not expect users to become proficient in working with `out` or `ref` parameters. > [!NOTE] -> When you work with parameters that are large structures, the additional resources that are required to copy these structures could cause a performance effect when you pass by value. In these cases, you might consider using `ref` or `out` parameters. +> When you work with parameters that are large structures, the additional resources that are required to copy these structures could have a performance effect when you pass by value. In these cases, you might consider using `ref` or `out` parameters. ## How to fix violations -To fix a violation of this rule that is caused by a value type, have the method return the object as its return value. If the method must return multiple values, redesign it to return a single instance of an object that holds the values. +To fix a violation of this rule that's caused by a value type, have the method return the object as its return value. If the method must return multiple values, redesign it to return a single instance of an object that holds the values. -To fix a violation of this rule that is caused by a reference type, make sure that the behavior that you want is to return a new instance of the reference. If it is, the method should use its return value to do this. +To fix a violation of this rule that's caused by a reference type, make sure that the behavior that you want is to return a new instance of the reference. If it is, the method should use its return value to do this. ## When to suppress warnings diff --git a/docs/fundamentals/code-analysis/quality-rules/ca1806.md b/docs/fundamentals/code-analysis/quality-rules/ca1806.md index 3a2fa43a3af4b..cb34fc9d91d65 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca1806.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca1806.md @@ -30,8 +30,8 @@ There are several possible reasons for this warning: - A new object is created but never used. - A method that creates and returns a new string is called and the new string is never used. -- A COM or P/Invoke method that returns a `HRESULT` or error code that's never used. -- A language-integrated query (LINQ) method that returns a result that's never used. +- A COM or P/Invoke method returns a `HRESULT` or error code that's never used. +- A language-integrated query (LINQ) method returns a result that's never used. ## Rule description @@ -110,33 +110,22 @@ dotnet_code_quality.CA1806.additional_use_results_methods = M:MyNamespace.MyType The following example shows a class that ignores the result of calling . :::code language="csharp" source="snippets/csharp/all-rules/ca1806.cs" id="snippet1"::: - :::code language="vb" source="snippets/vb/all-rules/ca1806-do-not-ignore-method-results_1.vb" id="snippet1"::: -## Example 2 - -The following example fixes the [Example 1](#example-1) violation by assigning the result of back to the variable it was called on. +The following example fixes the violation by assigning the result of back to the variable it was called on. :::code language="csharp" source="snippets/csharp/all-rules/ca1806.cs" id="snippet2"::: - :::code language="vb" source="snippets/vb/all-rules/ca1806-do-not-ignore-method-results_1.vb" id="snippet2"::: -## Example 3 +## Example 2 -The following example shows a method that does not use an object that it creates. +The following example shows a method that doesn't use an object that it creates. > [!NOTE] > This violation cannot be reproduced in Visual Basic. :::code language="csharp" source="snippets/csharp/all-rules/ca1806.cs" id="snippet3"::: -## Example 4 - -The following example fixes the [Example 3](#example-3) violation by removing the unnecessary creation of an object. +The following example fixes the violation by removing the unnecessary creation of an object. :::code language="csharp" source="snippets/csharp/all-rules/ca1806.cs" id="snippet4"::: - - diff --git a/docs/fundamentals/code-analysis/quality-rules/ca1816.md b/docs/fundamentals/code-analysis/quality-rules/ca1816.md index 07f6bf5e2ca7b..1e8d6d9a9530e 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca1816.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca1816.md @@ -36,7 +36,7 @@ Violations of this rule can be caused by: ## Rule description -The method lets users release resources at any time before the object becoming available for garbage collection. If the method is called, it frees resources of the object. This makes finalization unnecessary. should call so the garbage collector doesn't call the finalizer of the object. +The method lets users release resources at any time before the object becomes available for garbage collection. If the method is called, it frees resources of the object. This makes finalization unnecessary. should call so the garbage collector doesn't call the finalizer of the object. To prevent derived types with finalizers from having to reimplement and to call it, unsealed types without finalizers should still call . diff --git a/docs/fundamentals/code-analysis/quality-rules/ca2100.md b/docs/fundamentals/code-analysis/quality-rules/ca2100.md index b895770a83559..b8c91a24e89f5 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca2100.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca2100.md @@ -1,7 +1,7 @@ --- title: "CA2100: Review SQL queries for security vulnerabilities (code analysis)" description: "Learn about code analysis rule CA2100: Review SQL queries for security vulnerabilities" -ms.date: 11/04/2016 +ms.date: 09/24/2025 f1_keywords: - Review SQL queries for security vulnerabilities - ReviewSqlQueriesForSecurityVulnerabilities @@ -37,7 +37,7 @@ This rule assumes that any string whose value can't be determined at compile tim - Use a parameterized command string. - Validate the user input for both type and content before you build the command string. -The following .NET types implement the property or provide constructors that set the property by using a string argument. +The following .NET types implement the property or provide constructors that set the property by using a string argument: - and - and @@ -64,7 +64,7 @@ To fix a violation of this rule, use a parameterized query. ## When to suppress warnings -It is safe to suppress a warning from this rule if the command text does not contain any user input. +It's safe to suppress a warning from this rule if the command text does not contain any user input. ## Suppress a warning diff --git a/docs/fundamentals/code-analysis/quality-rules/ca2235.md b/docs/fundamentals/code-analysis/quality-rules/ca2235.md index c78e2eb1ac1b0..4263fab2efcb3 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca2235.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca2235.md @@ -67,7 +67,6 @@ For more information, see [How to suppress code analysis warnings](../suppress-w The following example shows two types: one that violates the rule and one that satisfies the rule. :::code language="csharp" source="snippets/csharp/all-rules/ca2235.cs" id="snippet1"::: - :::code language="vb" source="snippets/vb/all-rules/ca2235-mark-all-non-serializable-fields_1.vb"::: ## Remarks diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/.editorconfig b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/.editorconfig index 328b08ee34f0b..4edc25e7a4f19 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/.editorconfig +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/.editorconfig @@ -17,3 +17,6 @@ dotnet_diagnostic.CA1822.severity = none # CA2200: Rethrow to preserve stack details dotnet_diagnostic.CA2200.severity = suggestion + +# CA2100 +dotnet_diagnostic.CA2100.severity = warning diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1002.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1002.cs index 3440b130d48c3..9a8b8b4eb6c62 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1002.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1002.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace ca1001 { @@ -8,7 +8,7 @@ public class MutableItems { // CA1002: Change 'List' in 'MutableItems.Items' to // use 'Collection', 'ReadOnlyCollection' or 'KeyedCollection'. - public List Items { get; } = new List(); + public List Items { get; } = []; public void Add(string item) { @@ -19,7 +19,7 @@ public void Add(string item) // This class satisfies the rule. public class ReadOnlyItems { - private readonly List _items = new List(); + private readonly List _items = []; public IReadOnlyCollection Items => _items.AsReadOnly(); diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1024.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1024.cs index f0bae365c94fc..402c6dbcc5c71 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1024.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1024.cs @@ -92,7 +92,7 @@ public DateTime GetScheduleTime() // Time-consuming method that is called by GetCustomerHistory. Appointment[] LoadHistoryFromDB(long customerID) { - ArrayList records = new ArrayList(); + ArrayList records = []; // Load from database. return (Appointment[])records.ToArray(); } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1031.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1031.cs index c2de821540bda..2615245d86035 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1031.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1031.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace ca1031 @@ -7,14 +7,14 @@ namespace ca1031 // Creates two violations of the rule. public class GenericExceptionsCaught { - FileStream? inStream; - FileStream? outStream; + private readonly FileStream? _inStream; + private readonly FileStream? _outStream; public GenericExceptionsCaught(string inFile, string outFile) { try { - inStream = File.Open(inFile, FileMode.Open); + _inStream = File.Open(inFile, FileMode.Open); } catch (SystemException) { @@ -23,7 +23,7 @@ public GenericExceptionsCaught(string inFile, string outFile) try { - outStream = File.Open(outFile, FileMode.Open); + _outStream = File.Open(outFile, FileMode.Open); } catch { @@ -34,28 +34,28 @@ public GenericExceptionsCaught(string inFile, string outFile) public class GenericExceptionsCaughtFixed { - FileStream? inStream; - FileStream outStream; + private readonly FileStream? _inStream; + private readonly FileStream _outStream; public GenericExceptionsCaughtFixed(string inFile, string outFile) { try { - inStream = File.Open(inFile, FileMode.Open); + _inStream = File.Open(inFile, FileMode.Open); } // Fix the first violation by catching a specific exception. catch (FileNotFoundException) { Console.WriteLine($"Unable to open {inFile}."); - }; + } // For functionally equivalent code, also catch // remaining exceptions that may be thrown by File.Open try { - outStream = File.Open(outFile, FileMode.Open); + _outStream = File.Open(outFile, FileMode.Open); } // Fix the second violation by rethrowing the generic diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1032.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1032.cs index 37dae1e859621..8b5ca1a42d892 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1032.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1032.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace ca1032 { diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1036.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1036.cs index 939daf9bf15ff..43edcdaafdeee 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1036.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1036.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; // @@ -99,11 +99,11 @@ public override int GetHashCode() } public static bool operator <(RatingInformation left, RatingInformation right) { - return (Compare(left, right) < 0); + return Compare(left, right) < 0; } public static bool operator >(RatingInformation left, RatingInformation right) { - return (Compare(left, right) > 0); + return Compare(left, right) > 0; } } // diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1045.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1045.cs index cab84b003229e..2e69200538816 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1045.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1045.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace ca1045 { @@ -26,11 +26,12 @@ public class BadRefAndOut public static bool ReplyInformation(TypeOfFeedback input, out string reply, ref Actions action) { - bool returnReply = false; - string replyText = "Your feedback has been forwarded " + - "to the product manager."; + string replyText = """ + Your feedback has been forwarded to the product manager. + """; - reply = String.Empty; + reply = string.Empty; + bool returnReply; switch (input) { case TypeOfFeedback.Complaint: @@ -57,66 +58,33 @@ public static bool ReplyInformation(TypeOfFeedback input, // Redesigned version does not use out or ref parameters; // instead, it returns this container type. - public class ReplyData + public record class ReplyData(string Reply, Actions Action, bool ReturnReply = false) { - string reply; - Actions action; - bool returnReply; - - // Constructors. - public ReplyData() - { - this.reply = String.Empty; - this.action = Actions.Discard; - this.returnReply = false; - } - - public ReplyData(Actions action, string reply, bool returnReply) - { - this.reply = reply; - this.action = action; - this.returnReply = returnReply; - } - - // Properties. - public string Reply { get { return reply; } } - public Actions Action { get { return action; } } - public override string ToString() { - return String.Format("Reply: {0} Action: {1} return? {2}", - reply, action.ToString(), returnReply.ToString()); + return string.Format("Reply: {0} Action: {1} return? {2}", + Reply, Action.ToString(), ReturnReply.ToString()); } } public class RedesignedRefAndOut { - public static ReplyData ReplyInformation(TypeOfFeedback input) + public static ReplyData? ReplyInformation(TypeOfFeedback input) { - ReplyData answer; string replyText = "Your feedback has been forwarded " + "to the product manager."; - - switch (input) + ReplyData? answer = input switch { - case TypeOfFeedback.Complaint: - case TypeOfFeedback.Praise: - answer = new ReplyData( - Actions.ForwardToManagement, - "Thank you. " + replyText, - true); - break; - case TypeOfFeedback.Suggestion: - answer = new ReplyData( - Actions.ForwardToDeveloper, - replyText, - true); - break; - case TypeOfFeedback.Incomprehensible: - default: - answer = new ReplyData(); - break; - } + TypeOfFeedback.Complaint or TypeOfFeedback.Praise => new ReplyData( + "Thank you. " + replyText, + Actions.ForwardToManagement, + true), + TypeOfFeedback.Suggestion => new ReplyData( + replyText, + Actions.ForwardToDeveloper, + true), + _ => null, + }; return answer; } } @@ -133,13 +101,13 @@ static void UseTheComplicatedClass() string[] reply = new string[5]; // You must initialize a ref parameter. - Actions[] action = {Actions.Unknown,Actions.Unknown, + Actions[] action = [Actions.Unknown,Actions.Unknown, Actions.Unknown,Actions.Unknown, - Actions.Unknown,Actions.Unknown}; + Actions.Unknown,Actions.Unknown]; bool[] disposition = new bool[5]; int i = 0; - foreach (TypeOfFeedback t in Enum.GetValues(typeof(TypeOfFeedback))) + foreach (TypeOfFeedback t in Enum.GetValues()) { // The call to the library. disposition[i] = BadRefAndOut.ReplyInformation( @@ -153,7 +121,7 @@ static void UseTheSimplifiedClass() { ReplyData[] answer = new ReplyData[5]; int i = 0; - foreach (TypeOfFeedback t in Enum.GetValues(typeof(TypeOfFeedback))) + foreach (TypeOfFeedback t in Enum.GetValues()) { // The call to the library. answer[i] = RedesignedRefAndOut.ReplyInformation(t); diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1046.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1046.cs index 3848b3cfafab2..ed6831938086c 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1046.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1046.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace ca1046 { @@ -24,8 +24,8 @@ public class ReferenceTypeEquality { public static void Main1046() { - MyReferenceType a = new MyReferenceType(2, 2); - MyReferenceType b = new MyReferenceType(2, 2); + MyReferenceType a = new(2, 2); + MyReferenceType b = new(2, 2); MyReferenceType c = a; Console.WriteLine($"a = new {a} and b = new {b} are equal? {(a.Equals(b) ? "Yes" : "No")}"); diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1050.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1050.cs index 75b0d245895f1..29f2cc10df858 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1050.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1050.cs @@ -30,10 +30,10 @@ public class MainHolder { public static void Main1050() { - Test t1 = new Test(); + Test t1 = new(); Console.WriteLine(t1.ToString()); - ca1050.Test t2 = new ca1050.Test(); + ca1050.Test t2 = new(); Console.WriteLine(t2.ToString()); } } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1051.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1051.cs index 9b84c83d488a2..3c8660379d2f8 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1051.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1051.cs @@ -9,13 +9,7 @@ public class BadPublicInstanceFields public class GoodPublicInstanceFields { - private int instanceData = 32; - - public int InstanceData - { - get { return instanceData; } - set { instanceData = value; } - } + public int InstanceData { get; set; } = 32; } // } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1060.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1060.cs index d4018a7e60e34..a047e6902733f 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1060.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1060.cs @@ -1,7 +1,6 @@ using System.ComponentModel; using System.Runtime.InteropServices; using System.Security; -using System.Security.Permissions; namespace ca1060 { diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1061.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1061.cs index bcb34247e1271..40a328d887976 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1061.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1061.cs @@ -34,7 +34,7 @@ class Test { static void Main1061() { - DerivedType derived = new DerivedType(); + DerivedType derived = new(); // Calls DerivedType.MethodOne. derived.MethodOne("string1", "string2"); diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1420.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1420.cs index 0bca32fcf8b96..042e98a81bae2 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1420.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1420.cs @@ -1,5 +1,5 @@ +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; [assembly: DisableRuntimeMarshalling] @@ -7,5 +7,5 @@ class C { // Violates rule CA1420. [DllImport("NativeLibrary", SetLastError = true)] - public static extern void MyMethod (); + public static extern void MyMethod(); } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1806.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1806.cs index 9607f78cc07a2..021414888b23e 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1806.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1806.cs @@ -3,23 +3,15 @@ // public class Book { - private readonly string? _Title; - public Book(string title) { - if (title != null) - { - // Violates this rule - title.Trim(); - } + // Violates this rule. + title?.Trim(); - _Title = title; + Title = title; } - public string? Title - { - get { return _Title; } - } + public string? Title { get; } } // } @@ -29,22 +21,13 @@ namespace ca1806_2 // public class Book { - private readonly string? _Title; - public Book(string title) { - if (title != null) - { - title = title.Trim(); - } - - _Title = title; + // Fixes the violation. + Title = title?.Trim(); } - public string? Title - { - get { return _Title; } - } + public string? Title { get; } } // } @@ -54,13 +37,11 @@ namespace ca1806_3 // public class Book { - public Book() - { - } + public Book() { } public static Book CreateBook() { - // Violates this rule + // Violates this rule. new Book(); return new Book(); } @@ -73,12 +54,11 @@ namespace ca1806_4 // public class Book { - public Book() - { - } + public Book() { } public static Book CreateBook() { + // Fixes the violation. return new Book(); } } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1810.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1810.cs index 3098a86426924..947f357db1855 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1810.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1810.cs @@ -14,7 +14,7 @@ static StaticConstructor() { someInteger = 3; ResourceManager stringManager = - new ResourceManager("strings", Assembly.GetExecutingAssembly()); + new("strings", Assembly.GetExecutingAssembly()); resourceString = stringManager.GetString("string"); } @@ -32,7 +32,7 @@ public class NoStaticConstructor static string? InitializeResourceString() { ResourceManager stringManager = - new ResourceManager("strings", Assembly.GetExecutingAssembly()); + new("strings", Assembly.GetExecutingAssembly()); return stringManager.GetString("string"); } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1813.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1813.cs index 9fcba5ce71f59..5975b8f9e544d 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1813.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1813.cs @@ -7,19 +7,12 @@ namespace ca1813 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public sealed class DeveloperAttribute : Attribute { - private string nameValue; public DeveloperAttribute(string name) { - nameValue = name; + Name = name; } - public string Name - { - get - { - return nameValue; - } - } + public string Name { get; } } // } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1816.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1816.cs index 8796bc6d5b653..c60d5af04f7e3 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1816.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1816.cs @@ -1,12 +1,12 @@ using System; -using System.Data.SqlClient; +using System.IO; namespace ca1816 { // - public class DatabaseConnector : IDisposable + public class MyStreamClass : IDisposable { - private SqlConnection? _Connection = new SqlConnection(); + private MemoryStream? _stream = new(); public void Dispose() { @@ -18,11 +18,8 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - if (_Connection != null) - { - _Connection.Dispose(); - _Connection = null; - } + _stream?.Dispose(); + _stream = null; } } } @@ -32,9 +29,9 @@ protected virtual void Dispose(bool disposing) namespace ca1816_2 { // - public class DatabaseConnector : IDisposable + public class MyStreamClass : IDisposable { - private SqlConnection? _Connection = new SqlConnection(); + private MemoryStream? _stream = new(); public void Dispose() { @@ -46,11 +43,8 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - if (_Connection != null) - { - _Connection.Dispose(); - _Connection = null; - } + _stream?.Dispose(); + _stream = null; } } } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1819.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1819.cs index fa69ef07f939b..6163cec8f9c16 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1819.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca1819.cs @@ -5,17 +5,12 @@ namespace ca1819 // public class Book { - private string[] _Pages; - public Book(string[] pages) { - _Pages = pages; + Pages = pages; } - public string[] Pages - { - get { return _Pages; } - } + public string[] Pages { get; } } // } @@ -47,16 +42,12 @@ namespace ca1819_3 // public class Book { - private ReadOnlyCollection _Pages; public Book(string[] pages) { - _Pages = new ReadOnlyCollection(pages); + Pages = new ReadOnlyCollection(pages); } - public ReadOnlyCollection Pages - { - get { return _Pages; } - } + public ReadOnlyCollection Pages { get; } } // } @@ -66,18 +57,12 @@ namespace ca1819_4 // public class Book { - private string[] _Pages; - public Book(string[] pages) { - _Pages = pages; + Pages = pages; } - public string[] Pages - { - get { return _Pages; } - set { _Pages = value; } - } + public string[] Pages { get; set; } } // } @@ -87,17 +72,12 @@ namespace ca1819_5 // public class Book { - private Collection _Pages; - public Book(string[] pages) { - _Pages = new Collection(pages); + Pages = new Collection(pages); } - public Collection Pages - { - get { return _Pages; } - } + public Collection Pages { get; } } // } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2002.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2002.cs index ac06488f4ce78..4a71e590052ba 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2002.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2002.cs @@ -14,7 +14,7 @@ void LockOnWeakId1() void LockOnWeakId2() { - MemoryStream stream = new MemoryStream(); + MemoryStream stream = new(); lock (stream) { } } @@ -30,7 +30,7 @@ void LockOnWeakId4() } void LockOnWeakId5() { - OutOfMemoryException outOfMemory = new OutOfMemoryException(); + OutOfMemoryException outOfMemory = new(); lock (outOfMemory) { } } } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2022.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2022.cs index 7efa307d45ea8..9419abe1d4265 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2022.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2022.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Threading; namespace ca2022; diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2100.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2100.cs index 89e1d35ab5fa9..99fb5c2dbfcdc 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2100.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2100.cs @@ -1,16 +1,17 @@ -using System.Data; -using System.Data.SqlClient; +using System.Data.OleDb; +using System.Runtime.Versioning; namespace ca2100 { // - public class SqlQueries + [SupportedOSPlatform("Windows")] + public class OleDbQueries { public object UnsafeQuery( string connection, string name, string password) { - SqlConnection someConnection = new SqlConnection(connection); - SqlCommand someCommand = new SqlCommand(); + using OleDbConnection someConnection = new(connection); + using OleDbCommand someCommand = new(); someCommand.Connection = someConnection; someCommand.CommandText = "SELECT AccountNumber FROM Users " + @@ -26,14 +27,14 @@ public object UnsafeQuery( public object SaferQuery( string connection, string name, string password) { - SqlConnection someConnection = new SqlConnection(connection); - SqlCommand someCommand = new SqlCommand(); + using OleDbConnection someConnection = new(connection); + using OleDbCommand someCommand = new(); someCommand.Connection = someConnection; someCommand.Parameters.Add( - "@username", SqlDbType.NChar).Value = name; + "@username", OleDbType.Char).Value = name; someCommand.Parameters.Add( - "@password", SqlDbType.NChar).Value = password; + "@password", OleDbType.Char).Value = password; someCommand.CommandText = "SELECT AccountNumber FROM Users " + "WHERE Username=@username AND Password=@password"; @@ -44,11 +45,12 @@ public object SaferQuery( } } + [SupportedOSPlatform("Windows")] class MaliciousCode { static void Main2100(string[] args) { - SqlQueries queries = new SqlQueries(); + OleDbQueries queries = new(); queries.UnsafeQuery(args[0], "' OR 1=1 --", "[PLACEHOLDER]"); // Resultant query (which is always true): // SELECT AccountNumber FROM Users WHERE Username='' OR 1=1 diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2200.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2200.cs index 0330140d2d67f..b8edbdfe0821e 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2200.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2200.cs @@ -7,7 +7,7 @@ class TestsRethrow { static void Main2200() { - TestsRethrow testRethrow = new TestsRethrow(); + TestsRethrow testRethrow = new(); testRethrow.CatchException(); } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2213.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2213.cs index dac128725bc4a..b08362ce7baea 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2213.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2213.cs @@ -33,7 +33,7 @@ public void Dispose() public class TypeB : IDisposable { // Assume this type has some unmanaged resources. - TypeA aFieldOfADisposableType = new TypeA(); + TypeA aFieldOfADisposableType = new(); private bool disposed = false; protected virtual void Dispose(bool disposing) diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2214.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2214.cs index 0683515678163..890beff4bfb10 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2214.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2214.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace ca2214 { @@ -37,7 +37,7 @@ public class TestBadlyConstructedType { public static void Main2214() { - DerivedType derivedInstance = new DerivedType(); + DerivedType derivedInstance = new(); } } // diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2227.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2227.cs index d1752afa5a0b9..71f2858835f32 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2227.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2227.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections; +using System.Collections; namespace ca2227 { @@ -25,13 +24,14 @@ class ReplaceWritableCollection { static void Main2227() { - ArrayList newCollection = new ArrayList(new string[] { "a", "new", "collection" }); + ArrayList newCollection = ["a", "new", "collection"]; - WritableCollection collection = new WritableCollection(); - - // This line of code demonstrates how the entire collection - // can be replaced by a property that's not read only. - collection.SomeStrings = newCollection; + WritableCollection collection = new() + { + // This line of code demonstrates how the entire collection + // can be replaced by a property that's not read only. + SomeStrings = newCollection + }; // If the intent is to replace an entire collection, // implement and/or use the Clear() and AddRange() methods instead. diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2231.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2231.cs index 545772f5107de..8ddac37ef0245 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2231.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2231.cs @@ -30,7 +30,7 @@ public override bool Equals(object? obj) return false; PointWithoutHash p = (PointWithoutHash)obj; - return ((this.x == p.x) && (this.y == p.y)); + return (this.x == p.x) && (this.y == p.y); } } // diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2234.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2234.cs index 5a7b5f239b3bb..65abc5d8337f5 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2234.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2234.cs @@ -11,7 +11,7 @@ internal void AddToHistory(Uri uriType) { } public class Browser { - History uriHistory = new History(); + History uriHistory = new(); public void ErrorProne() { @@ -22,7 +22,7 @@ public void SaferWay() { try { - Uri newUri = new Uri("http://www.adventure-works.com"); + Uri newUri = new("http://www.adventure-works.com"); uriHistory.AddToHistory(newUri); } catch (UriFormatException) { } diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2235.cs b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2235.cs index 485b3bf5993ee..8f7fc537ed202 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2235.cs +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/csharp/all-rules/ca2235.cs @@ -3,37 +3,21 @@ namespace ca2235 { // - public class Mouse + public class Mouse(int numberOfButtons, string scanType) { - int buttons; - string scanTypeValue; - - public int NumberOfButtons - { - get { return buttons; } - } - - public string ScanType - { - get { return scanTypeValue; } - } - - public Mouse(int numberOfButtons, string scanType) - { - buttons = numberOfButtons; - scanTypeValue = scanType; - } + public int NumberOfButtons { get; } = numberOfButtons; + public string ScanType { get; } = scanType; } [Serializable] public class InputDevices1 { // Violates MarkAllNonSerializableFields. - Mouse opticalMouse; + readonly Mouse _opticalMouse; public InputDevices1() { - opticalMouse = new Mouse(5, "optical"); + _opticalMouse = new Mouse(5, "optical"); } } @@ -42,11 +26,11 @@ public class InputDevices2 { // Satisfies MarkAllNonSerializableFields. [NonSerialized] - Mouse opticalMouse; + readonly Mouse _opticalMouse; public InputDevices2() { - opticalMouse = new Mouse(5, "optical"); + _opticalMouse = new Mouse(5, "optical"); } } // diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/.editorconfig b/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/.editorconfig index 7f80d2f28f742..8766482eea080 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/.editorconfig +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/.editorconfig @@ -2,3 +2,4 @@ dotnet_diagnostic.CA1420.severity = suggestion dotnet_diagnostic.CA1422.severity = suggestion dotnet_diagnostic.CA2200.severity = suggestion +dotnet_diagnostic.CA2100.severity = warning diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca1816-call-gc-suppressfinalize-correctly_1.vb b/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca1816-call-gc-suppressfinalize-correctly_1.vb index b204b93e14c7b..8704eac036439 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca1816-call-gc-suppressfinalize-correctly_1.vb +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca1816-call-gc-suppressfinalize-correctly_1.vb @@ -1,25 +1,24 @@ -Imports System -Imports System.Data.SqlClient +Imports System.IO Namespace ca1816 ' - Public Class DatabaseConnector + Public Class MyStreamClass Implements IDisposable - Private _Connection As New SqlConnection + Private _stream As New MemoryStream Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) - ' Violates rules + ' Violates rule. GC.SuppressFinalize(True) End Sub Protected Overridable Sub Dispose(ByVal disposing As Boolean) If disposing Then - If _Connection IsNot Nothing Then - _Connection.Dispose() - _Connection = Nothing + If _stream IsNot Nothing Then + _stream.Dispose() + _stream = Nothing End If End If End Sub @@ -31,10 +30,10 @@ End Namespace Namespace ca1816_2 ' - Public Class DatabaseConnector + Public Class MyStreamClass Implements IDisposable - Private _Connection As New SqlConnection + Private _stream As New MemoryStream Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) @@ -43,9 +42,9 @@ Namespace ca1816_2 Protected Overridable Sub Dispose(ByVal disposing As Boolean) If disposing Then - If _Connection IsNot Nothing Then - _Connection.Dispose() - _Connection = Nothing + If _stream IsNot Nothing Then + _stream.Dispose() + _stream = Nothing End If End If End Sub diff --git a/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca2100-review-sql-queries-for-security-vulnerabilities_1.vb b/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca2100-review-sql-queries-for-security-vulnerabilities_1.vb index cf153ce265a3d..ae9b24a4002f2 100644 --- a/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca2100-review-sql-queries-for-security-vulnerabilities_1.vb +++ b/docs/fundamentals/code-analysis/quality-rules/snippets/vb/all-rules/ca2100-review-sql-queries-for-security-vulnerabilities_1.vb @@ -1,20 +1,21 @@ -Imports System -Imports System.Data -Imports System.Data.SqlClient +Imports System.Data +Imports System.Data.OleDb +Imports System.Runtime.Versioning Namespace ca2100 Public Class SqlQueries + Function UnsafeQuery(connection As String, name As String, password As String) As Object - Dim someConnection As New SqlConnection(connection) - Dim someCommand As New SqlCommand() - someCommand.Connection = someConnection - - someCommand.CommandText = "SELECT AccountNumber FROM Users " & - "WHERE Username='" & name & "' AND Password='" & password & "'" + Dim someConnection As New OleDbConnection(connection) + Dim someCommand As New OleDbCommand With { + .Connection = someConnection, + .CommandText = "SELECT AccountNumber FROM Users " & + "WHERE Username='" & name & "' AND Password='" & password & "'" + } someConnection.Open() Dim accountNumber As Object = someCommand.ExecuteScalar() @@ -23,17 +24,19 @@ Namespace ca2100 End Function + Function SaferQuery(connection As String, name As String, password As String) As Object - Dim someConnection As New SqlConnection(connection) - Dim someCommand As New SqlCommand() - someCommand.Connection = someConnection + Dim someConnection As New OleDbConnection(connection) + Dim someCommand As New OleDbCommand With { + .Connection = someConnection + } - someCommand.Parameters.Add( - "@username", SqlDbType.NChar).Value = name - someCommand.Parameters.Add( - "@password", SqlDbType.NChar).Value = password + someCommand.Parameters.AddWithValue( + "@username", OleDbType.Char).Value = name + someCommand.Parameters.AddWithValue( + "@password", OleDbType.Char).Value = password someCommand.CommandText = "SELECT AccountNumber FROM Users " & "WHERE Username=@username AND Password=@password" @@ -48,6 +51,7 @@ Namespace ca2100 Class MaliciousCode + Shared Sub Main2100(args As String()) Dim queries As New SqlQueries() diff --git a/docs/navigate/devops-testing/toc.yml b/docs/navigate/devops-testing/toc.yml index 57a6ce1b45d6c..9356d2a89e479 100644 --- a/docs/navigate/devops-testing/toc.yml +++ b/docs/navigate/devops-testing/toc.yml @@ -227,6 +227,16 @@ items: href: ../../core/testing/mstest-analyzers/mstest0049.md - name: MSTEST0050 href: ../../core/testing/mstest-analyzers/mstest0050.md + - name: MSTEST0051 + href: ../../core/testing/mstest-analyzers/mstest0051.md + - name: MSTEST0052 + href: ../../core/testing/mstest-analyzers/mstest0052.md + - name: MSTEST0053 + href: ../../core/testing/mstest-analyzers/mstest0053.md + - name: MSTEST0054 + href: ../../core/testing/mstest-analyzers/mstest0054.md + - name: MSTEST0055 + href: ../../core/testing/mstest-analyzers/mstest0055.md - name: Test platforms items: - name: Microsoft.Testing.Platform diff --git a/docs/navigate/tools-diagnostics/toc.yml b/docs/navigate/tools-diagnostics/toc.yml index 57d0df4e2781b..3b3baf4179a0e 100644 --- a/docs/navigate/tools-diagnostics/toc.yml +++ b/docs/navigate/tools-diagnostics/toc.yml @@ -190,6 +190,8 @@ items: href: ../../core/tools/dotnet-package-remove.md - name: dotnet package search href: ../../core/tools/dotnet-package-search.md + - name: dotnet package update + href: ../../core/tools/dotnet-package-update.md - name: dotnet publish href: ../../core/tools/dotnet-publish.md - name: dotnet reference add/list/remove @@ -241,8 +243,12 @@ items: href: ../../core/tools/dotnet-workload.md - name: dotnet workload sets href: ../../core/tools/dotnet-workload-sets.md + - name: dotnet workload clean + href: ../../core/tools/dotnet-workload-clean.md - name: dotnet workload config href: ../../core/tools/dotnet-workload-config.md + - name: dotnet workload history + href: ../../core/tools/dotnet-workload-history.md - name: dotnet workload install href: ../../core/tools/dotnet-workload-install.md - name: dotnet workload list diff --git a/docs/standard/serialization/system-text-json/customize-properties.md b/docs/standard/serialization/system-text-json/customize-properties.md index 71306e558d4c3..9e01189b86e95 100644 --- a/docs/standard/serialization/system-text-json/customize-properties.md +++ b/docs/standard/serialization/system-text-json/customize-properties.md @@ -259,7 +259,7 @@ Use built-in serialization attributes. Here's a more complete version of the example that includes a simple class. ```copilot-prompt -Take this C# class: +Take this C# class: public class WeatherForecast { public DateTime Date { get; set; } @@ -273,7 +273,9 @@ change the serialized property name to use underscores between words. Use built-in serialization attributes. ``` -GitHub Copilot is powered by AI, so surprises and mistakes are possible. For more information, see [Copilot FAQs](https://aka.ms/copilot-general-use-faqs). +Review Copilot's suggestions before applying them. + +For more information about GitHub Copilot, see GitHub's [FAQs](https://github.com/features/copilot#faq). ## See also diff --git a/docs/standard/serialization/system-text-json/deserialization.md b/docs/standard/serialization/system-text-json/deserialization.md index 20dd8146495a2..03cee6a725127 100644 --- a/docs/standard/serialization/system-text-json/deserialization.md +++ b/docs/standard/serialization/system-text-json/deserialization.md @@ -99,7 +99,9 @@ Map property names & values. Provide example output. ``` -GitHub Copilot is powered by AI, so surprises and mistakes are possible. For more information, see [Copilot FAQs](https://aka.ms/copilot-general-use-faqs). +Review Copilot's suggestions before applying them. + +For more information about GitHub Copilot, see GitHub's [FAQs](https://github.com/features/copilot#faq). ## See also diff --git a/docs/standard/serialization/system-text-json/how-to.md b/docs/standard/serialization/system-text-json/how-to.md index babb9fb35a68d..aa0e4de0688fe 100644 --- a/docs/standard/serialization/system-text-json/how-to.md +++ b/docs/standard/serialization/system-text-json/how-to.md @@ -120,7 +120,9 @@ The object contains the following fields: FirstName (string), Lastname (string), Provide example output. ``` -GitHub Copilot is powered by AI, so surprises and mistakes are possible. For more information, see [Copilot FAQs](https://aka.ms/copilot-general-use-faqs). +Review Copilot's suggestions before applying them. + +For more information about GitHub Copilot, see GitHub's [FAQs](https://github.com/features/copilot#faq). ## See also diff --git a/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md b/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md index cd458b56d5d0d..cadca3a4c1e25 100644 --- a/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md +++ b/docs/standard/serialization/system-text-json/migrate-from-newtonsoft.md @@ -2,7 +2,7 @@ title: "Migrate from Newtonsoft.Json to System.Text.Json - .NET" description: "Learn about the differences between Newtonsoft.Json and System.Text.Json and how to migrate to System.Text.Json." no-loc: [System.Text.Json, Newtonsoft.Json] -ms.date: 02/11/2025 +ms.date: 10/03/2025 helpviewer_keywords: - "JSON serialization" - "serializing objects" @@ -24,7 +24,7 @@ The `System.Text.Json` namespace provides functionality for serializing to and d * .NET Core 2.0, 2.1, and 2.2 > [!TIP] -> You can use AI assistance to [migrate from `Newtonsoft.Json`](#use-ai-to-migrate). +> You can use AI assistance to [migrate from `Newtonsoft.Json`](#use-ai-for-solution-wide-migration). `System.Text.Json` focuses primarily on performance, security, and standards compliance. It has some key differences in default behavior and doesn't aim to have feature parity with `Newtonsoft.Json`. For some scenarios, `System.Text.Json` currently has no built-in functionality, but there are recommended workarounds. For other scenarios, workarounds are impractical. @@ -639,26 +639,24 @@ System.Text.Json sets limits that can't be changed for some values, such as the Newtonsoft parses `NaN`, `Infinity`, and `-Infinity` JSON string tokens. With System.Text.Json, use . For information about how to use this setting, see [Allow or write numbers in quotes](invalid-json.md#allow-or-write-numbers-in-quotes). -## Use AI to migrate +## Use AI for solution-wide migration -You can use AI tools, such as GitHub Copilot, to migrate your code from `Newtonsoft.Json` to `System.Text.Json` within your IDE. You can customize the prompt per your requirements. +You can use AI tools, such as GitHub Copilot, to help with migrating your solution code from `Newtonsoft.Json` to `System.Text.Json`. -**Example prompt for Copilot Chat** +Here's an example prompt you can use in Visual Studio Copilot Chat to migrate a solution. ```copilot-prompt -convert the following code to use System.Text.Json -Product product = new Product(); - -product.Name = "Apple"; -product.ExpiryDate = new DateTime(2024, 08, 08); -product.Price = 3.99M; -product.Sizes = new string[] { "Small", "Medium", "Large" }; - -string output = JsonConvert.SerializeObject(product); -Console.WriteLine(output); +Convert all serialization code in this #solution from Newtonsoft.Json to System.Text.Json, using the recommended approach for my current .NET version. +- Update attributes and properties, including rules for skipping or renaming during serialization +- Ensure polymorphic serialization continues to work correctly +- Respect existing custom converters and project-level settings (for example, from the Utilities folder or appsettings.json) +- Update related unit tests and highlight any potential breaking changes +- Generate a migration summary ``` -GitHub Copilot is powered by AI, so surprises and mistakes are possible. For more information, see [Copilot FAQs](https://aka.ms/copilot-general-use-faqs). +Review Copilot's suggestions before applying them. + +For more information about GitHub Copilot, see GitHub's [FAQs](https://github.com/features/copilot#faq). ## Additional resources diff --git a/samples/snippets/fsharp/compiler-messages/fs0067.fsx b/samples/snippets/fsharp/compiler-messages/fs0067.fsx new file mode 100644 index 0000000000000..6f5eb276bdef3 --- /dev/null +++ b/samples/snippets/fsharp/compiler-messages/fs0067.fsx @@ -0,0 +1,18 @@ +(* Redundant Type test *) +type Dog() = + member this.Bark() = printfn "Woof!" + +let dog = Dog() + +if dog :? Dog then + dog.Bark() + +(* Redundant Downcast *) +type Cat(name: string) = + member this.Name = name + +let cat = Cat("Kitten") + +let sameCat = cat :?> Cat + +printfn "It's still a %s" sameCat.Name diff --git a/samples/snippets/fsharp/fssamples.fsproj b/samples/snippets/fsharp/fssamples.fsproj index d8280b01cbfad..13eaf156f581d 100644 --- a/samples/snippets/fsharp/fssamples.fsproj +++ b/samples/snippets/fsharp/fssamples.fsproj @@ -1,13 +1,22 @@ - + Exe - net8.0 + net9.0 preview + + + + + + + + + diff --git a/samples/snippets/fsharp/lang-ref-2/snippet4818.fs b/samples/snippets/fsharp/lang-ref-2/snippet4818.fs new file mode 100644 index 0000000000000..f5092e160eafa --- /dev/null +++ b/samples/snippets/fsharp/lang-ref-2/snippet4818.fs @@ -0,0 +1,18 @@ +// Wildcard pattern matching "nothing" examples + +// Example 1: Wildcard ignoring function parameters +let ignoreAllParams _ _ = "Ignores all input" + +// Example 2: Wildcard in destructuring, ignoring elements +let getFirstOnly (first, _) = first + +// Example 3: Using wildcard to ignore optional values +let handleEmpty opt = + match opt with + | Some _ -> "Has something" + | None -> "Has nothing" + +// Usage +printfn "%s" (ignoreAllParams 42 "test") +printfn "%d" (getFirstOnly (1, "ignored")) +printfn "%s" (handleEmpty None) \ No newline at end of file diff --git a/samples/snippets/fsharp/query-expressions/basic-query.fs b/samples/snippets/fsharp/query-expressions/basic-query.fs new file mode 100644 index 0000000000000..31c36a4c5b448 --- /dev/null +++ b/samples/snippets/fsharp/query-expressions/basic-query.fs @@ -0,0 +1,79 @@ +module QueryExpressions.BasicQuery + +// Basic query expression example using in-memory data +open System +open System.Linq + +// Define simple data types to represent our sample database +type Student = { + StudentID: int + Name: string + Age: int option +} + +type Course = { + CourseID: int + CourseName: string +} + +type CourseSelection = { + ID: int + StudentID: int + CourseID: int +} + +// Sample data +let students = [ + { StudentID = 1; Name = "Abercrombie, Kim"; Age = Some 10 } + { StudentID = 2; Name = "Abolrous, Hazen"; Age = Some 14 } + { StudentID = 3; Name = "Hance, Jim"; Age = Some 12 } + { StudentID = 4; Name = "Adams, Terry"; Age = Some 12 } + { StudentID = 5; Name = "Hansen, Claus"; Age = Some 11 } + { StudentID = 6; Name = "Penor, Lori"; Age = Some 13 } + { StudentID = 7; Name = "Perham, Tom"; Age = Some 12 } + { StudentID = 8; Name = "Peng, Yun-Feng"; Age = None } +] + +let courses = [ + { CourseID = 1; CourseName = "Algebra I" } + { CourseID = 2; CourseName = "Trigonometry" } + { CourseID = 3; CourseName = "Algebra II" } + { CourseID = 4; CourseName = "History" } + { CourseID = 5; CourseName = "English" } + { CourseID = 6; CourseName = "French" } + { CourseID = 7; CourseName = "Chinese" } +] + +let courseSelections = [ + { ID = 1; StudentID = 1; CourseID = 2 } + { ID = 2; StudentID = 1; CourseID = 3 } + { ID = 3; StudentID = 1; CourseID = 5 } + { ID = 4; StudentID = 2; CourseID = 2 } + { ID = 5; StudentID = 2; CourseID = 5 } + { ID = 6; StudentID = 2; CourseID = 6 } + { ID = 7; StudentID = 2; CourseID = 3 } + { ID = 8; StudentID = 3; CourseID = 2 } + { ID = 9; StudentID = 3; CourseID = 1 } + { ID = 10; StudentID = 4; CourseID = 2 } + { ID = 11; StudentID = 4; CourseID = 5 } + { ID = 12; StudentID = 4; CourseID = 2 } + { ID = 13; StudentID = 5; CourseID = 3 } + { ID = 14; StudentID = 5; CourseID = 2 } + { ID = 15; StudentID = 7; CourseID = 3 } +] + +// Convert to queryable collections for LINQ operations +let studentsQueryable = students.AsQueryable() +let coursesQueryable = courses.AsQueryable() +let courseSelectionsQueryable = courseSelections.AsQueryable() + +// A query expression example +let query1 = + query { + for student in studentsQueryable do + select student + } + +// Print results +query1 +|> Seq.iter (fun student -> printfn "Student: %s" student.Name) \ No newline at end of file diff --git a/samples/snippets/fsharp/query-expressions/modern-example.fs b/samples/snippets/fsharp/query-expressions/modern-example.fs new file mode 100644 index 0000000000000..803ad83187c44 --- /dev/null +++ b/samples/snippets/fsharp/query-expressions/modern-example.fs @@ -0,0 +1,38 @@ +module QueryExpressions.ModernExample + +// Modern F# query expression example using in-memory collections +// This provides a working alternative to deprecated type providers + +open System +open System.Linq + +// Simple data structures to demonstrate query capabilities +type Customer = { + CustomerID: int + CompanyName: string + ContactName: string +} + +// Sample data that replaces the external data source +let customers = [ + { CustomerID = 1; CompanyName = "Alfreds Futterkiste"; ContactName = "Maria Anders" } + { CustomerID = 2; CompanyName = "Ana Trujillo Emparedados y helados"; ContactName = "Ana Trujillo" } + { CustomerID = 3; CompanyName = "Antonio Moreno Taquería"; ContactName = "Antonio Moreno" } + { CustomerID = 4; CompanyName = "Around the Horn"; ContactName = "Thomas Hardy" } + { CustomerID = 5; CompanyName = "Berglunds snabbköp"; ContactName = "Christina Berglund" } +] + +// Convert to queryable for LINQ operations +let db = customers.AsQueryable() + +// A query expression that works with modern .NET +let query1 = + query { + for customer in db do + select customer + } + +// Print results (this would be equivalent to the old example) +let printResults() = + query1 + |> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName) \ No newline at end of file diff --git a/samples/snippets/fsharp/query-expressions/snippet1.fs b/samples/snippets/fsharp/query-expressions/snippet1.fs new file mode 100644 index 0000000000000..52a838be5fef0 --- /dev/null +++ b/samples/snippets/fsharp/query-expressions/snippet1.fs @@ -0,0 +1,45 @@ +// F# query expression example using Entity Framework Core +open System +open System.Linq +open Microsoft.EntityFrameworkCore + +// Entity type +[] +type Customer = { + CustomerID: int + CompanyName: string + ContactName: string +} + +// Database context +type NorthwindContext() = + inherit DbContext() + + [] + val mutable private customers: DbSet + member this.Customers with get() = this.customers and set v = this.customers <- v + + override _.OnConfiguring(optionsBuilder: DbContextOptionsBuilder) = + optionsBuilder.UseInMemoryDatabase("NorthwindDatabase") |> ignore + +// Create and seed database +let db = + let context = new NorthwindContext() + context.Customers.AddRange([| + { CustomerID = 1; CompanyName = "Alfreds Futterkiste"; ContactName = "Maria Anders" } + { CustomerID = 2; CompanyName = "Ana Trujillo Emparedados y helados"; ContactName = "Ana Trujillo" } + { CustomerID = 3; CompanyName = "Antonio Moreno Taquería"; ContactName = "Antonio Moreno" } + |]) |> ignore + context.SaveChanges() |> ignore + context + +// A query expression +let query1 = + query { + for customer in db.Customers do + select customer + } + +// Print results +query1 +|> Seq.iter (fun customer -> printfn "Company: %s Contact: %s" customer.CompanyName customer.ContactName) \ No newline at end of file diff --git a/samples/snippets/fsharp/query-expressions/snippet2.fs b/samples/snippets/fsharp/query-expressions/snippet2.fs new file mode 100644 index 0000000000000..e9e296594b86b --- /dev/null +++ b/samples/snippets/fsharp/query-expressions/snippet2.fs @@ -0,0 +1,97 @@ +// Database setup example for query expressions using Entity Framework Core +open System +open System.Linq +open Microsoft.EntityFrameworkCore + +// Define entity types that map to database tables +[] +type Student = { + StudentID: int + Name: string + Age: Nullable +} + +[] +type Course = { + CourseID: int + CourseName: string +} + +[] +type CourseSelection = { + ID: int + StudentID: int + CourseID: int +} + +// Define the database context using Entity Framework Core +type SchoolContext() = + inherit DbContext() + + [] + val mutable private student: DbSet + member this.Student with get() = this.student and set v = this.student <- v + + [] + val mutable private course: DbSet + member this.Course with get() = this.course and set v = this.course <- v + + [] + val mutable private courseSelection: DbSet + member this.CourseSelection with get() = this.courseSelection and set v = this.courseSelection <- v + + override _.OnConfiguring(optionsBuilder: DbContextOptionsBuilder) = + optionsBuilder.UseInMemoryDatabase("SchoolDatabase") |> ignore + +// Create and seed the database +let createAndSeedDatabase() = + let context = new SchoolContext() + + // Seed data + context.Student.AddRange([| + { StudentID = 1; Name = "Abercrombie, Kim"; Age = Nullable(10) } + { StudentID = 2; Name = "Abolrous, Hazen"; Age = Nullable(14) } + { StudentID = 3; Name = "Hance, Jim"; Age = Nullable(12) } + { StudentID = 4; Name = "Adams, Terry"; Age = Nullable(12) } + { StudentID = 5; Name = "Hansen, Claus"; Age = Nullable(11) } + { StudentID = 6; Name = "Penor, Lori"; Age = Nullable(13) } + { StudentID = 7; Name = "Perham, Tom"; Age = Nullable(12) } + { StudentID = 8; Name = "Peng, Yun-Feng"; Age = Nullable() } + |]) |> ignore + + context.Course.AddRange([| + { CourseID = 1; CourseName = "Algebra I" } + { CourseID = 2; CourseName = "Trigonometry" } + { CourseID = 3; CourseName = "Algebra II" } + { CourseID = 4; CourseName = "History" } + { CourseID = 5; CourseName = "English" } + { CourseID = 6; CourseName = "French" } + { CourseID = 7; CourseName = "Chinese" } + |]) |> ignore + + context.CourseSelection.AddRange([| + { ID = 1; StudentID = 1; CourseID = 2 } + { ID = 2; StudentID = 1; CourseID = 3 } + { ID = 3; StudentID = 1; CourseID = 5 } + { ID = 4; StudentID = 2; CourseID = 2 } + { ID = 5; StudentID = 2; CourseID = 5 } + { ID = 6; StudentID = 2; CourseID = 6 } + { ID = 7; StudentID = 2; CourseID = 3 } + { ID = 8; StudentID = 3; CourseID = 2 } + { ID = 9; StudentID = 3; CourseID = 1 } + { ID = 10; StudentID = 4; CourseID = 2 } + { ID = 11; StudentID = 4; CourseID = 5 } + { ID = 12; StudentID = 4; CourseID = 2 } + { ID = 13; StudentID = 5; CourseID = 3 } + { ID = 14; StudentID = 5; CourseID = 2 } + { ID = 15; StudentID = 7; CourseID = 3 } + |]) |> ignore + + context.SaveChanges() |> ignore + context + +// Create the database context +let db = createAndSeedDatabase() + +// Needed for some query operator examples: +let data = [ 1; 5; 7; 11; 18; 21] \ No newline at end of file