

 适用于 .NET 的 AWS SDK V3 已进入维护模式。

我们建议您迁移到 [适用于 .NET 的 AWS SDK V4](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/welcome.html)。有关如何迁移的更多详细信息和信息，请参阅我们的[维护模式公告](https://aws.amazon.com/blogs/developer/aws-sdk-for-net-v3-maintenance-mode-announcement/)。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用中的 AWS 服务 适用于 .NET 的 AWS SDK
<a name="working-with-aws-services"></a>

以下各节包含示例、教程、任务和指南，向您展示如何使用 适用于 .NET 的 AWS SDK 来处理 AWS 服务。这些示例和教程依赖于 适用于 .NET 的 AWS SDK 提供的 API。要查看 API 中有哪些类和方法可用，请参阅[适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)。

如果您不熟悉 适用于 .NET 的 AWS SDK，则可能需要先查看该[快速了解](quick-start.md)主题。其中提供了对软件开发工具包的简单介绍。

您可以在代码示例[存储库和 [awslabs](https://github.com/awslabs/aws-sdk-net-samples) 存储库中找到更多AWS 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3)。 GitHub

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

**Topics**
+ [带有指导的代码示例](tutorials-examples.md)
+ [AWS Lambda](aws-lambda.md)
+ [高级库和框架](high-level-libraries.md)
+ [其它服务和配置](other-apis-intro.md)

# 带有指导的代码示例 适用于 .NET 的 AWS SDK
<a name="tutorials-examples"></a>

以下各节包含代码示例，并提供示例指导。他们可以帮助您学习如何使用 适用于 .NET 的 AWS SDK 来处理 AWS 服务。

如果您不熟悉 适用于 .NET 的 AWS SDK，则可能需要先查看该[快速了解](quick-start.md)主题。其中提供了对软件开发工具包的简单介绍。

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

**Topics**
+ [CloudFormation](cloudformation-apis-intro.md)
+ [Amazon Cognito](cognito-apis-intro.md)
+ [DynamoDB](dynamodb-intro.md)
+ [Amazon EC2](ec2-apis-intro.md)
+ [IAM](iam-apis-intro.md)
+ [Amazon S3](s3-apis-intro.md)
+ [Amazon SNS](sns-apis-intro.md)
+ [Amazon SQS](sqs-apis-intro.md)

# CloudFormation 使用访问 适用于 .NET 的 AWS SDK
<a name="cloudformation-apis-intro"></a>

这些 适用于 .NET 的 AWS SDK 支持 [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/)，可预测且重复地创建和配置 AWS 基础设施部署。

## APIs
<a name="w2aac19c15c11b5"></a>

为 CloudFormation 客户 适用于 .NET 的 AWS SDK APIs 提供服务。 APIs 使您能够使用模板和堆栈等 CloudFormation 功能。本节包含少量示例，向您展示使用这些示例时可以遵循的模式 APIs。要查看全套内容 APIs，请参阅 [适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)（并滚动至 “Amazon. CloudFormation“)。

 AWS CloudFormation APIs 它们由提供[AWSSDK。 CloudFormation](https://www.nuget.org/packages/AWSSDK.CloudFormation/)包裹。

## 先决条件
<a name="w2aac19c15c11b7"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

## 主题
<a name="w2aac19c15c11b9"></a>

**Topics**
+ [APIs](#w2aac19c15c11b5)
+ [先决条件](#w2aac19c15c11b7)
+ [主题](#w2aac19c15c11b9)
+ [列出 AWS 资源](cfn-list-resources.md)

# 使用列出 AWS 资源 AWS CloudFormation
<a name="cfn-list-resources"></a>

此示例向您展示如何使用列 适用于 .NET 的 AWS SDK 出 CloudFormation 堆栈中的资源。该示例使用低级别 API。该应用程序不带任何参数，只是收集用户凭证可以访问的所有堆栈的信息，然后显示有关这些堆栈的信息。

## SDK 参考
<a name="w2aac19c15c11c13b5b1"></a>

NuGet 包裹：
+ [AWSSDK.CloudFormation](https://www.nuget.org/packages/AWSSDK.CloudFormation/)

编程元素：
+ 命名空间 [Amazon。 CloudFormation](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/NCloudFormation.html)

  班级 [AmazonCloudFormationClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/TCloudFormationClient.html)
+ 命名空间 [Amazon。 CloudFormation.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/NCloudFormationModel.html)

  班级[ICloudFormationPaginatorFactory。 DescribeStacks](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/MICloudFormationPaginatorFactoryDescribeStacksDescribeStacksRequest.html)

  班级 [DescribeStackResourcesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/TDescribeStackResourcesRequest.html)

  班级 [DescribeStackResourcesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/TDescribeStackResourcesResponse.html)

  类 [Stack](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/TStack.html)

  班级 [StackResource](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/TStackResource.html)

  类 [Tag](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CloudFormation/TTag.html)

```
using Amazon.CloudFormation;
using Amazon.CloudFormation.Model;
using Amazon.Runtime;

namespace CloudFormationActions;

public static class HelloCloudFormation
{
    public static IAmazonCloudFormation _amazonCloudFormation;

    static async Task Main(string[] args)
    {
        // Create the CloudFormation client
        _amazonCloudFormation = new AmazonCloudFormationClient();
        Console.WriteLine($"\nIn Region: {_amazonCloudFormation.Config.RegionEndpoint}");

        // List the resources for each stack
        await ListResources();
    }

    /// <summary>
    /// Method to list stack resources and other information.
    /// </summary>
    /// <returns>True if successful.</returns>
    public static async Task<bool> ListResources()
    {
        try
        {
            Console.WriteLine("Getting CloudFormation stack information...");

            // Get all stacks using the stack paginator.
            var paginatorForDescribeStacks =
                _amazonCloudFormation.Paginators.DescribeStacks(
                    new DescribeStacksRequest());
            await foreach (Stack stack in paginatorForDescribeStacks.Stacks)
            {
                // Basic information for each stack
                Console.WriteLine("\n------------------------------------------------");
                Console.WriteLine($"\nStack: {stack.StackName}");
                Console.WriteLine($"  Status: {stack.StackStatus.Value}");
                Console.WriteLine($"  Created: {stack.CreationTime}");

                // The tags of each stack (etc.)
                if (stack.Tags.Count > 0)
                {
                    Console.WriteLine("  Tags:");
                    foreach (Tag tag in stack.Tags)
                        Console.WriteLine($"    {tag.Key}, {tag.Value}");
                }

                // The resources of each stack
                DescribeStackResourcesResponse responseDescribeResources =
                    await _amazonCloudFormation.DescribeStackResourcesAsync(
                        new DescribeStackResourcesRequest
                        {
                            StackName = stack.StackName
                        });
                if (responseDescribeResources.StackResources.Count > 0)
                {
                    Console.WriteLine("  Resources:");
                    foreach (StackResource resource in responseDescribeResources
                                 .StackResources)
                        Console.WriteLine(
                            $"    {resource.LogicalResourceId}: {resource.ResourceStatus}");
                }
            }

            Console.WriteLine("\n------------------------------------------------");
            return true;
        }
        catch (AmazonCloudFormationException ex)
        {
            Console.WriteLine("Unable to get stack information:\n" + ex.Message);
            return false;
        }
        catch (AmazonServiceException ex)
        {
            if (ex.Message.Contains("Unable to get IAM security credentials"))
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("If you are usnig SSO, be sure to install" +
                                  " the AWSSDK.SSO and AWSSDK.SSOOIDC packages.");
            }
            else
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return false;
        }
        catch (ArgumentNullException ex)
        {
            if (ex.Message.Contains("Options property cannot be empty: ClientName"))
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("If you are using SSO, have you logged in?");
            }
            else
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return false;
        }
    }
```

# 使用 Amazon Cognito 验证用户身份
<a name="cognito-apis-intro"></a>

**注意**  
本主题中的信息特定于基于.NET Framework 和 3.3 及更早 适用于 .NET 的 AWS SDK 版本的项目。

使用 Amazon Cognito Identity，您可以为用户创建唯一身份，并对他们进行身份验证，以便安全访问您的 AWS 资源，例如亚马逊 S3 或 Amazon DynamoDB。Amazon Cognito Identity 支持公共身份提供商（如 Amazon、Facebook、Twitter/Digits、Google 或兼容 OpenID Connect 的任何提供商），以及未经身份验证的身份。Amazon Cognito 还支持[已经过开发人员验证的身份](https://aws.amazon.com/blogs/mobile/amazon-cognito-announcing-developer-authenticated-identities/)，借助该身份，您可以注册用户并通过自己的后端身份验证流程对用户进行身份验证，同时仍然使用 Amazon Cognito Sync 同步用户数据和访问 AWS 资源。

有关 [Amazon Cognito](https://aws.amazon.com/cognito/) 的更多信息，请参阅 [Amazon Cognito 开发人员指南](https://docs.aws.amazon.com/cognito/latest/developerguide/)

以下代码示例显示如何轻松使用 Amazon Cognito 身份。[凭证提供程序](cognito-creds-provider.md)示例显示如何创建用户身份并对其进行身份验证。该[CognitoAuthentication 扩展库](cognito-authentication-extension.md)示例展示了如何使用 CognitoAuthentication 扩展库对 Amazon Cognito 用户池进行身份验证。

**Topics**
+ [凭证提供程序](cognito-creds-provider.md)
+ [CognitoAuthentication 扩展库](cognito-authentication-extension.md)

# Amazon Cognito 凭证提供程序
<a name="cognito-creds-provider"></a>

**注意**  
本主题中的信息特定于基于.NET Framework 和 3.3 及更早 适用于 .NET 的 AWS SDK 版本的项目。

 `Amazon.CognitoIdentity.CognitoAWSCredentials`，可在[AWSSDK。 CognitoIdentity](https://www.nuget.org/packages/AWSSDK.CognitoIdentity/) NuGetpack AWS age，是一个凭证对象，它使用 Amazon Cognito 和 AWS Security Token Service (AWS STS) 来检索用于拨打电话的证书。

设置 `CognitoAWSCredentials` 的第一步是创建一个“身份池”。(身份池是用于存储特定于您的账户的用户身份信息的存储区。此信息可跨各种客户端平台、设备和操作系统进行检索，因此，如果用户在手机上开始使用此应用程序不久后又切换到平板电脑，该用户仍然可以使用保留的应用程序信息。您可以通过 Amazon Cognito 控制台创建新的身份池。如果您使用的是此控制台，它还将为您提供您所需的其他信息：
+ 您的账号 - 一个 12 位数字，如 123456789012，这是您账户独有的号码。
+ 未经身份验证的角色 ARN - 未经身份验证的用户将担任的角色。例如，此角色可提供对您的数据的只读权限。
+ 经过身份验证的角色 ARN- 经过身份验证的用户将担任的角色。此角色可提供对您的数据的更广泛的权限。

## 设置 Cognito AWSCredentials
<a name="set-up-cognitoawscredentials"></a>

以下代码示例显示如何设置 `CognitoAWSCredentials`，然后您可使用此凭证以未经身份验证的角色的身份调用 Amazon S3。这使您能够只需要对用户进行身份验证所需的最少量数据即可进行调用。用户权限由角色控制，因此您可以根据需要配置访问权限。

```
CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    accountId,        // Account number
    identityPoolId,   // Identity pool ID
    unAuthRoleArn,    // Role for unauthenticated users
    null,             // Role for authenticated users, not set
    region);
using (var s3Client = new AmazonS3Client(credentials))
{
    s3Client.ListBuckets();
}
```

## AWS 以未经身份验证的用户身份使用
<a name="use-aws-as-an-unauthenticated-user"></a>

以下代码示例显示了如何以未经身份验证的用户身份开始使用 AWS ，然后通过 Facebook 进行身份验证并更新凭据以使用 Facebook 凭据。使用此方法，您可以通过经过身份验证的角色授予经过身份验证的用户不同的功能。例如，您可能有一个电话应用程序，该应用程序允许用户匿名查看内容，但在用户使用一个或多个已配置的提供商登录的情况下允许其发帖。

```
CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    accountId, identityPoolId,
    unAuthRoleArn,    // Role for unauthenticated users
    authRoleArn,      // Role for authenticated users
    region);
using (var s3Client = new AmazonS3Client(credentials))
{
    // Initial use will be unauthenticated
    s3Client.ListBuckets();

    // Authenticate user through Facebook
    string facebookToken = GetFacebookAuthToken();

    // Add Facebook login to credentials. This clears the current AWS credentials
    // and retrieves new AWS credentials using the authenticated role.
    credentials.AddLogin("graph.facebook.com", facebookAccessToken);

    // This call is performed with the authenticated role and credentials
    s3Client.ListBuckets();
}
```

如果您将 `CognitoAWSCredentials` 对象与属于 适用于 .NET 的 AWS SDK的一部分的 `AmazonCognitoSyncClient` 结合使用，则该对象会提供更多功能。如果您使用的是 `AmazonCognitoSyncClient` 和 `CognitoAWSCredentials`，则无需在使用 `IdentityPoolId` 进行调用时指定 `IdentityId` 和 `AmazonCognitoSyncClient` 属性。这些属性会自动从 `CognitoAWSCredentials` 中进行填入。以下代码示例将对此进行说明以及说明每当 `IdentityId` 的 `CognitoAWSCredentials` 发生更改时通知您的事件。在某些情况下 `IdentityId` 可能会发生更改，例如，当从未经身份验证的用户更改为经过身份验证的用户时。

```
CognitoAWSCredentials credentials = GetCognitoAWSCredentials();

// Log identity changes
credentials.IdentityChangedEvent += (sender, args) =>
{
    Console.WriteLine("Identity changed: [{0}] => [{1}]", args.OldIdentityId, args.NewIdentityId);
};

using (var syncClient = new AmazonCognitoSyncClient(credentials))
{
    var result = syncClient.ListRecords(new ListRecordsRequest
    {
        DatasetName = datasetName
        // No need to specify these properties
        //IdentityId = "...",
        //IdentityPoolId = "..."        
    });
}
```

# Amazon CognitoAuthentication 扩展库示例
<a name="cognito-authentication-extension"></a>

**注意**  
本主题中的信息特定于基于.NET Framework 和 3.3 及更早 适用于 .NET 的 AWS SDK 版本的项目。

 CognitoAuthentication 扩展程序库，可[在 Amazon.Extensions 中找到。 CognitoAuthentication](https://www.nuget.org/packages/Amazon.Extensions.CognitoAuthentication/) NuGet 包，简化了.NET Core 和 Xamarin 开发人员的 Amazon Cognito 用户池的身份验证过程。该库基于 Amazon Cognito 身份提供商 API 构建，可创建和发送用户身份验证 API 调用。

## 使用 CognitoAuthentication 扩展库
<a name="using-the-cognitoauthentication-extension-library"></a>

Amazon Cognito 有一些适用于标准身份验证流程的内置 `AuthFlow` 和 `ChallengeName` 值，以通过安全远程密码（SRP）协议验证用户名和密码。有关身份验证流程的更多信息，请参阅 [Amazon Cognito 用户池身份验证流程](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html)。

以下示例需要这些 `using` 语句：

```
// Required for all examples
using System;
using Amazon;
using Amazon.CognitoIdentity;
using Amazon.CognitoIdentityProvider;
using Amazon.Extensions.CognitoAuthentication;
using Amazon.Runtime;
// Required for the GetS3BucketsAsync example
using Amazon.S3;
using Amazon.S3.Model;
```

### 用户基本身份验证
<a name="use-basic-authentication"></a>

[AmazonCognitoIdentityProviderClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/CognitoIdentityProvider/TCognitoIdentityProviderClient.html)使用 An [onymou](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Runtime/TAnonymousAWSCredentials.html) s 创建AWSCredentials，它不需要签名请求。您无需提供一个区域，如果未提供区域，底层代码将调用 `FallbackRegionFactory.GetRegionEndpoint()`。创建 `CognitoUserPool` 和 `CognitoUser` 对象。使用包含用户密码的 `StartWithSrpAuthAsync` 调用 `InitiateSrpAuthRequest` 方法。

```
public static async void GetCredsAsync()
{
    AmazonCognitoIdentityProviderClient provider =
        new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials());
    CognitoUserPool userPool = new CognitoUserPool("poolID", "clientID", provider);
    CognitoUser user = new CognitoUser("username", "clientID", userPool, provider);
    InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest()
    {
        Password = "userPassword"
    };

    AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
    accessToken = authResponse.AuthenticationResult.AccessToken;

}
```

### 使用质询进行身份验证
<a name="authenticate-with-challenges"></a>

通过质询（例如使用 NewPasswordRequired 和多因素身份验证 (MFA)）来继续进行身份验证流程也更加简单。唯一的要求是 CognitoAuthentication 对象、用户的 SRP 密码以及下一个挑战的必要信息，这些信息是在提示用户输入后获取的。以下代码显示了一种在身份验证流程中检查质询类型并获得 MFA 和 NewPasswordRequired 质询的相应响应的方法。

像之前一样执行基本身份验证请求，并且 `await` 一个 `AuthFlowResponse`。当收到响应时，循环访问返回的 `AuthenticationResult` 对象。如果 `ChallengeName` 类型为 `NEW_PASSWORD_REQUIRED`，请调用 `RespondToNewPasswordRequiredAsync` 方法。

```
public static async void GetCredsChallengesAsync()
{
    AmazonCognitoIdentityProviderClient provider = 
        new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials());
    CognitoUserPool userPool = new CognitoUserPool("poolID", "clientID", provider);
    CognitoUser user = new CognitoUser("username", "clientID", userPool, provider);
    InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest(){
        Password = "userPassword"
    };

    AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);

    while (authResponse.AuthenticationResult == null)
    {
        if (authResponse.ChallengeName == ChallengeNameType.NEW_PASSWORD_REQUIRED)
        {
            Console.WriteLine("Enter your desired new password:");
            string newPassword = Console.ReadLine();

            authResponse = await user.RespondToNewPasswordRequiredAsync(new RespondToNewPasswordRequiredRequest()
            {
                SessionID = authResponse.SessionID,
                NewPassword = newPassword
            });
            accessToken = authResponse.AuthenticationResult.AccessToken;
        }
        else if (authResponse.ChallengeName == ChallengeNameType.SMS_MFA)
        {
            Console.WriteLine("Enter the MFA Code sent to your device:");
            string mfaCode = Console.ReadLine();

            AuthFlowResponse mfaResponse = await user.RespondToSmsMfaAuthAsync(new RespondToSmsMfaRequest()
            {
                SessionID = authResponse.SessionID,
                MfaCode = mfaCode

            }).ConfigureAwait(false);
            accessToken = authResponse.AuthenticationResult.AccessToken;
        }
        else
        {
            Console.WriteLine("Unrecognized authentication challenge.");
            accessToken = "";
            break;
        }
    }

    if (authResponse.AuthenticationResult != null)
    {
        Console.WriteLine("User successfully authenticated.");
    }
    else
    {
        Console.WriteLine("Error in authentication process.");
    }
 
}
```

### 身份验证后使用 AWS 资源
<a name="use-aws-resources-after-authentication"></a>

使用 CognitoAuthentication 库对用户进行身份验证后，下一步就是允许该用户访问相应的 AWS 资源。为此，您必须通过 Amazon Cognito 联合身份控制台创建一个身份池。通过将您创建的 Amazon Cognito 用户池指定为提供商并使用其 poolID 和 clientID，您可以允许您的 Amazon Cognito 用户群体用户访问连接到您账户的 AWS 资源。您还可以指定不同的角色来支持未经身份验证的和已经过身份验证的用户访问不同的资源。您可以在 IAM 控制台中更改这些规则，其中，您可以在角色的附加策略的**操作**字段中添加或删除权限。然后，使用相应的身份池、用户池和 Amazon Cognito 用户信息，您可以调用不同的 AWS 资源。以下示例显示了使用用于访问关联的身份池的角色允许的不同 Amazon S3 桶的 SRP 进行用户身份验证

```
public async void GetS3BucketsAsync()
{
    var provider = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials());
    CognitoUserPool userPool = new CognitoUserPool("poolID", "clientID", provider);
    CognitoUser user = new CognitoUser("username", "clientID", userPool, provider);

    string password = "userPassword";

    AuthFlowResponse context = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest()
    {
        Password = password
    }).ConfigureAwait(false);

    CognitoAWSCredentials credentials =
        user.GetCognitoAWSCredentials("identityPoolID", RegionEndpoint.< YourIdentityPoolRegion >);

    using (var client = new AmazonS3Client(credentials))
    {
        ListBucketsResponse response =
            await client.ListBucketsAsync(new ListBucketsRequest()).ConfigureAwait(false);

        foreach (S3Bucket bucket in response.Buckets)
        {
            Console.WriteLine(bucket.BucketName);
        }
    }
}
```

## 更多身份验证选项
<a name="more-authentication-options"></a>

除了 SRP NewPasswordRequired、和 MFA 之外，扩展库还为 CognitoAuthentication 以下用户提供了更简单的身份验证流程：
+ 自定义- 通过调用 `StartWithCustomAuthAsync(InitiateCustomAuthRequest customRequest)` 启动 
+ RefreshToken -首先致电至 `StartWithRefreshTokenAuthAsync(InitiateRefreshTokenAuthRequest refreshTokenRequest)` 
+ RefreshTokenSRP-通过拨打以下电话启动 `StartWithRefreshTokenAuthAsync(InitiateRefreshTokenAuthRequest refreshTokenRequest)` 
+ AdminNoSRP-通过拨打以下电话启动 `StartWithAdminNoSrpAuthAsync(InitiateAdminNoSrpAuthRequest adminAuthRequest)` 

根据您想要的流程调用相应的方法。然后，当质询显示在每个方法调用的 `AuthFlowResponse` 对象中时继续提示用户进行质询。此外，调用相应的响应方法，如适用于 MFA 质询的 `RespondToSmsMfaAuthAsync` 和适用于自定义质询的 `RespondToCustomAuthAsync`。

# 使用 Amazon DynamoDB NoSQL 数据库
<a name="dynamodb-intro"></a>

**注意**  
这些主题中的编程模型同时存在于 .NET Framework 和 .NET (Core) 中，但是调用约定不同，无论是同步还是异步。

 适用于 .NET 的 AWS SDK 支持 Amazon DynamoDB，这是一项由提供的快速 NoSQL 数据库服务。 AWS开发工具包为与 DynamoDB 通信提供了三种编程模型：*低级*模型、*文档*模型和*对象持久性*模型。

以下信息介绍了这些模型及其模型 APIs，提供了如何以及何时使用它们的示例，并提供了指向中其他 DynamoDB 编程资源的链接。 适用于 .NET 的 AWS SDK

## 低级模型
<a name="dynamodb-intro-apis-low-level"></a>

低级编程模型封装对 DynamoDB 服务的直接调用。您可以通过 [Amazon.Dynamo DBv2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/NDynamoDBv2.html) 命名空间访问此模型。

在三种模型中，低级模型需要编写的代码最多。例如，您必须将 .NET 数据类型转换为 DynamoDB 中的对等类型。但是，此模型向您提供了对大部分功能的访问。

以下示例显示如何使用低级模型创建表、修改表以及将项目插入到 DynamoDB 中的表。

### 创建表
<a name="dynamodb-intro-apis-low-level-create-table"></a>

在以下示例中，您可以使用 `CreateTable` 类的 `AmazonDynamoDBClient` 方法创建表。`CreateTable` 方法使用包含特性的 `CreateTableRequest` 类的实例，例如必需的项目属性名称、主键定义和吞吐容量。该 `CreateTable` 方法返回 `CreateTableResponse` 类的实例。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();

Console.WriteLine("Getting list of tables");
List<string> currentTables = client.ListTables().TableNames;
Console.WriteLine("Number of tables: " + currentTables.Count);
if (!currentTables.Contains("AnimalsInventory"))
{
    var request = new CreateTableRequest
    {
        TableName = "AnimalsInventory",
        AttributeDefinitions = new List<AttributeDefinition>
      {
        new AttributeDefinition
        {
          AttributeName = "Id",
          // "S" = string, "N" = number, and so on.
          AttributeType = "N"
        },
        new AttributeDefinition
        {
          AttributeName = "Type",
          AttributeType = "S"
        }
      },
        KeySchema = new List<KeySchemaElement>
      {
        new KeySchemaElement
        {
          AttributeName = "Id",
          // "HASH" = hash key, "RANGE" = range key.
          KeyType = "HASH"
        },
        new KeySchemaElement
        {
          AttributeName = "Type",
          KeyType = "RANGE"
        },
      },
        ProvisionedThroughput = new ProvisionedThroughput
        {
            ReadCapacityUnits = 10,
            WriteCapacityUnits = 5
        },
    };

    var response = client.CreateTable(request);

    Console.WriteLine("Table created with request ID: " +
      response.ResponseMetadata.RequestId);
}
```

### 验证该表已准备好修改
<a name="dynamodb-intro-apis-low-level-verify-table"></a>

您必须先准备好表进行修改，然后才能更改或修改表。以下示例显示了如何使用低级模型验证 DynamoDB 中的表已准备就绪。在此示例中，通过 `DescribeTable` 类的 `AmazonDynamoDBClient` 方法引用要检查的目标表。该代码每五秒检查一次表的 `TableStatus` 属性的值。当状态设置为 `ACTIVE` 时，表已准备好供修改。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();      
var status = "";

do
{
  // Wait 5 seconds before checking (again).
  System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
        
  try
  {
    var response = client.DescribeTable(new DescribeTableRequest
    {
      TableName = "AnimalsInventory"
    });

    Console.WriteLine("Table = {0}, Status = {1}",
      response.Table.TableName,
      response.Table.TableStatus);

    status = response.Table.TableStatus;
  }
  catch (ResourceNotFoundException)
  {
    // DescribeTable is eventually consistent. So you might
    //   get resource not found. 
  }

} while (status != TableStatus.ACTIVE);
```

### 将项目插入到表中
<a name="dynamodb-intro-apis-low-level-insert-item"></a>

在以下示例中，您使用低级模型将两个项目插入到 DynamoDB 中的表。每个项目通过 `PutItem` 类的 `AmazonDynamoDBClient` 方法插入，使用 `PutItemRequest` 类的实例。`PutItemRequest` 类的两个实例中的每个实例都接受将要插入项目的表名以及一系列项目属性值。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();

var request1 = new PutItemRequest
{
  TableName = "AnimalsInventory",
  Item = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "1" }},
    { "Type", new AttributeValue { S = "Dog" }},
    { "Name", new AttributeValue { S = "Fido" }}
  }
};

var request2 = new PutItemRequest
{
  TableName = "AnimalsInventory",
  Item = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "2" }},
    { "Type", new AttributeValue { S = "Cat" }},
    { "Name", new AttributeValue { S = "Patches" }}
  }
};
        
client.PutItem(request1);
client.PutItem(request2);
```

## 文档模型
<a name="dynamodb-intro-apis-document"></a>

文档编程模型提供了一种更简单的方法来处理 DynamoDB 中的数据。此模型专门用于访问表和表中的项目。您可以通过 [Amazon. DBv2 Dynamo 访问此模型。 DocumentModel](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/NDynamoDBv2DocumentModel.html)命名空间。

与低级编程模型相比，使用文档模型更容易针对 DynamoDB 数据编写代码。例如，您无需将相同数量的 .NET 数据类型转换为 DynamoDB 中的对等类型。不过，使用此模型所能访问的功能没有低级编程模型所提供的多。例如，您可以使用此模型创建、检索、更新和删除表中的项目。但是，要创建表，您必须使用低级模型。与对象持久化模型相比，此模型需要编写更多代码来存储、加载和查询 .NET 对象。

有关 DynamoDB 文档编程模型的更多信息，请参阅 [Amazon DynamoDB 开发人员指南](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/)中的 [.NET：文档模型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKMidLevel.html)。

以下各节提供有关如何创建所需的 DynamoDB 表的表示形式的信息，以及如何使用文档模型将项目插入表和从表中获取项目的示例。

### 创建表的表示形式
<a name="dynamodb-intro-apis-document-table"></a>

要使用文档模型执行数据操作，您必须首先创建 `Table` 类的实例，它会代表特定的表。有两种主要方式可执行此操作：

**LoadTable method**

第一种机制是使用 [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TTable.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TTable.html) 类的静态 `LoadTable` 方法之一，类似于以下示例：

```
var client = new AmazonDynamoDBClient();
Table table = Table.LoadTable(client, "Reply");
```

**注意**  
虽然这种机制起作用，但在某些条件下，由于冷启动和线程池行为，它有时会导致额外的延迟或死锁。有关这些行为的更多信息，请参阅博客文章 [Improved DynamoDB Initialization Patterns for the 适用于 .NET 的 AWS SDK](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/)。

**TableBuilder**

[ AWSSDK. DBv2 NuGet Dynamo 软件包的 3.7.203 版本中引入了另一种机制，即[https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TTableBuilder.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TTableBuilder.html)](https://www.nuget.org/packages/AWSSDK.DynamoDBv2/3.7.203)类。该机制可以通过删除某些隐式方法调用（特别是 `DescribeTable` 方法）来解决上述行为。此机制的使用方式类似于以下示例：

```
var client = new AmazonDynamoDBClient();
var table = new TableBuilder(client, "Reply")
    .AddHashKey("Id", DynamoDBEntryType.String)
    .AddRangeKey("ReplyDateTime", DynamoDBEntryType.String)
    .AddGlobalSecondaryIndex("PostedBy-Message-index", "Author", DynamoDBEntryType.String, "Message", DynamoDBEntryType.String)
    .Build();
```

有关此替代机制的更多信息，请再次参阅博客文章 [Improved DynamoDB Initialization Patterns for the 适用于 .NET 的 AWS SDK](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/)。

### 将项目插入到表中
<a name="dynamodb-intro-apis-document-insert-item"></a>

在以下示例中，回复通过 `Table` 类的 `PutItemAsync` 方法插入到 Reply 表中。`PutItemAsync` 方法获取 `Document` 类的实例；`Document` 类只是已初始化属性的集合。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;

// Create a representation of the "Reply" table
//  by using one of the mechanisms described previously.

// Then, add a reply to the table.
var newReply = new Document();
newReply["Id"] = Guid.NewGuid().ToString();
newReply["ReplyDateTime"] = DateTime.UtcNow;
newReply["PostedBy"] = "Author1";
newReply["Message"] = "Thank you!";

await table.PutItemAsync(newReply);
```

### 从表中获取项目
<a name="dynamodb-intro-apis-document-get-item"></a>

在以下示例中，通过 `Table` 类的 `GetItemAsync` 方法检索回复。为了确定要获得的回复，该`GetItemAsync`方法使用目标回复 hash-and-range的主键。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;

// Create a representation of the "Reply" table
//  by using one of the mechanisms described previously.

// Then, get a reply from the table
//  where "guid" is the hash key and "datetime" is the range key.
var reply = await table.GetItemAsync(guid, datetime);
Console.WriteLine("Id = " + reply["Id"]);
Console.WriteLine("ReplyDateTime = " + reply["ReplyDateTime"]);
Console.WriteLine("PostedBy = " + reply["PostedBy"]);
Console.WriteLine("Message = " + reply["Message"]);
```

前面的示例为 `WriteLine` 方法隐式将表值转换为字符串。您可以使用 `DynamoDBEntry` 类的多种“As[type]”方法执行显式转换。例如，对于 `Id` 的值，您可以通过 `AsGuid()` 方法将其从 `Primitive` 数据类型显式转换为 GUID：

```
var guid = reply["Id"].AsGuid();
```

## 对象持久化模型
<a name="dynamodb-intro-apis-object-persistence"></a>

对象持久性编程模型专门针对在 DynamoDB 中存储、加载和查询 .NET 对象而设计。您可以通过 [Amazon. DBv2 Dynamo 访问此模型。 DataModel](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/NDynamoDBv2DataModel.html)命名空间。

在三种模型中，使用对象持久性模型最容易针对要存储、加载或查询的 DynamoDB 数据编写代码。例如，您可以直接使用 DynamoDB 数据类型。但是，此模型仅支持在 DynamoDB 中存储、加载和查询 .NET 对象的操作。例如，您可以使用此模型创建、检索、更新和删除表中的项目。不过，您必须先使用低级模型创建表，然后使用此模型将 .NET 类映射到表。

有关 DynamoDB 对象持久性编程模型的更多信息，请参阅 [Amazon DynamoDB 开发人员指南](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/)中的 [.NET：对象持久性模型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html)。

以下示例向您演示如何定义表示 DynamoDB 项目的 .NET 类，使用 .NET 类的实例将项目插入到 DynamoDB ，以及使用 .NET 对象的实例从表获取项目。

### 定义表示表中项目的 .NET 类
<a name="dynamodb-intro-apis-object-persistence-net-class-item"></a>

在下面的类定义示例中，`DynamoDBTable`属性指定表名，而`DynamoDBHashKey`和`DynamoDBRangeKey`属性则建模表 hash-and-range的主键。定义 `DynamoDBGlobalSecondaryIndexHashKey` 属性的目的是为了可以构造对特定作者回复的查询。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

[DynamoDBTable("Reply")]
public class Reply
{
    [DynamoDBHashKey]
    public string Id { get; set; }

    [DynamoDBRangeKey(StoreAsEpoch = false)]
    public DateTime ReplyDateTime { get; set; }

    [DynamoDBGlobalSecondaryIndexHashKey("PostedBy-Message-Index",
        AttributeName ="PostedBy")]
    public string Author { get; set; }

    [DynamoDBGlobalSecondaryIndexRangeKey("PostedBy-Message-Index")]
    public string Message { get; set; }
}
```

### 为对象持久性模型创建上下文
<a name="dynamodb-intro-apis-object-persistence-context"></a>

要使用适用于 DynamoDB 的对象持久性编程模型，您需要创建一个上下文，它提供到 DynamoDB 的连接，让您能够访问表、执行各种操作，以及执行查询。

**基本上下文**

以下代码示例演示如何创建最基本的上下文 。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

var client = new AmazonDynamoDBClient();
var context = new DynamoDBContext(client);
```

**与 DisableFetchingTableMetadata 属性的上下文**

以下示例显示了如何额外设置 `DynamoDBContextConfig` 类的 `DisableFetchingTableMetadata` 属性以防止隐式调用 `DescribeTable` 方法。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

var client = new AmazonDynamoDBClient();
var context = new DynamoDBContext(client, new DynamoDBContextConfig
{
    DisableFetchingTableMetadata = true
});
```

如果将该 `DisableFetchingTableMetadata` 属性设置为 `false`（默认值），如第一个示例所示，则可以省略 `Reply` 类中描述表项键和索引结构的属性。相反，这些属性将通过对 `DescribeTable` 方法的隐式调用推断出来。如第二个示例所示，如果 `DisableFetchingTableMetadata` 设置为 `true`，则对象持久性模型的方法（例如 `SaveAsync` 和 `QueryAsync`）完全依赖于 `Reply` 类中定义的属性。在这种情况下，不会调用 `DescribeTable` 方法。

**注意**  
在某些情况下，由于冷启动和线程池行为，对 `DescribeTable` 方法的调用有时会导致额外的延迟或死锁。因此，有时避免调用该方法是有利的。  
有关这些行为的更多信息，请参阅博客文章 [Improved DynamoDB Initialization Patterns for the 适用于 .NET 的 AWS SDK](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/)。

### 使用 .NET 类的实例将项目插入到表中
<a name="dynamodb-intro-apis-object-persistence-net-insert-item"></a>

在本示例中，项目通过 `SaveAsync` 类的 `DynamoDBContext` 方法插入，该方法采用表示项目的 .NET 类的已初始化实例。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

// Create an appropriate context for the object persistence programming model,
//  examples of which have been described earlier.

// Create an object that represents the new item.
var reply = new Reply()
{
    Id = Guid.NewGuid().ToString(),
    ReplyDateTime = DateTime.UtcNow,
    Author = "Author1",
    Message = "Thank you!"
};

// Insert the item into the table.
await context.SaveAsync<Reply>(reply, new DynamoDBOperationConfig
{
    IndexName = "PostedBy-Message-index"
});
```

### 使用 .NET 类的实例从表中获取项目
<a name="dynamodb-intro-apis-object-persistence-net-get-item"></a>

在此示例中，使用 `DynamoDBContext` 类的 `QueryAsync` 方法创建了一个查询，用于查找“Author1”的所有记录。然后，通过查询的 `GetNextSetAsync` 方法检索项目。

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

// Create an appropriate context for the object persistence programming model,
//  examples of which have been described earlier.

// Construct a query that finds all replies by a specific author.
var query = context.QueryAsync<Reply>("Author1", new DynamoDBOperationConfig
{
    IndexName = "PostedBy-Message-index"
});

// Display the result.
var set = await query.GetNextSetAsync();
foreach (var item in set)
{
    Console.WriteLine("Id = " + item.Id);
    Console.WriteLine("ReplyDateTime = " + item.ReplyDateTime);
    Console.WriteLine("PostedBy = " + item.Author);
    Console.WriteLine("Message = " + item.Message);
}
```

### 有关对象持久性模型的其它信息
<a name="dynamodb-intro-apis-object-persistence-more-into"></a>

上面显示的示例和解释有时包括名为 `DisableFetchingTableMetadata` 的 `DynamoDBContext` 类的属性。此属性是在 [ AWSSDK.Dynamo DBv2 NuGet 包的 3.7.203 版本](https://www.nuget.org/packages/AWSSDK.DynamoDBv2/3.7.203)中引入的，允许您避免某些可能由于冷启动和线程池行为而导致额外延迟或死锁的情况。有关更多信息，请参阅博客文章 [Improved DynamoDB Initialization Patterns for the 适用于 .NET 的 AWS SDK](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/)。

以下是有关此属性的一些其它信息。
+ 如果您使用.NET Framework，则可以在 `app.config` 或 `web.config` 文件中全局设置此属性。
+ 可以使用 [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Amazon/TAWSConfigsDynamoDB.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Amazon/TAWSConfigsDynamoDB.html) 类全局设置此属性，如下面的示例所示。

  ```
  // Set the DisableFetchingTableMetadata property globally
  // before constructing any context objects.
  AWSConfigsDynamoDB.Context.DisableFetchingTableMetadata = true;
  
  var client = new AmazonDynamoDBClient();
  var context = new DynamoDBContext(client);
  ```
+ 在某些情况下，您无法将 DynamoDB 属性添加到 .NET 类中；例如，如果该类是在依赖项中定义的。在这种情况下，仍然可以利用 `DisableFetchingTableMetadata` 属性。为此，除了 `DisableFetchingTableMetadata` 属性之外，还要使用 [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TTableBuilder.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/DynamoDBv2/TTableBuilder.html) 类。该`TableBuilder`类也是在 [ AWSSDK.Dynamo 软件包的 3.7.203 版本中引入的。DBv2 NuGet ](https://www.nuget.org/packages/AWSSDK.DynamoDBv2/3.7.203)

  ```
  // Set the DisableFetchingTableMetadata property globally
  // before constructing any context objects.
  AWSConfigsDynamoDB.Context.DisableFetchingTableMetadata = true;
  
  var client = new AmazonDynamoDBClient();
  var context = new DynamoDBContext(client);
  
  var table = new TableBuilder(client, "Reply")
      .AddHashKey("Id", DynamoDBEntryType.String)
      .AddRangeKey("ReplyDateTime", DynamoDBEntryType.String)
      .AddGlobalSecondaryIndex("PostedBy-Message-index", "Author", DynamoDBEntryType.String,
          "Message", DynamoDBEntryType.String)
      .Build();
  
  // This registers the "Reply" table we constructed via the builder.
  context.RegisterTableDefinition(table);
  
  // Now operations like this will work,
  // even if the Reply class was not annotated with this index.
  var query = context.QueryAsync<Reply>("Author1", new DynamoDBOperationConfig()
  {
      IndexName = "PostedBy-Message-index"
  });
  ```

## 更多信息
<a name="dynamodb-intro-more-info"></a>

 **使用编程 DynamoDB、信息和示例 适用于 .NET 的 AWS SDK **
+  [DynamoDB APIs](http://blogs.aws.amazon.com/net/post/Tx17SQHVEMW8MXC/DynamoDB-APIs) 
+  [DynamoDB 系列入门](http://blogs.aws.amazon.com/net/post/Tx2XQOCY08QMTKO/DynamoDB-Series-Kickoff) 
+  [DynamoDB 系列 - 文档模型](http://blogs.aws.amazon.com/net/post/Tx2R0WG46GQI1JI/DynamoDB-Series-Document-Model) 
+  [DynamoDB 系列 - 转换架构](http://blogs.aws.amazon.com/net/post/Tx2TCOGWG7ARUH5/DynamoDB-Series-Conversion-Schemas) 
+  [DynamoDB 系列 - 对象持久化模型](http://blogs.aws.amazon.com/net/post/Tx20L86FLMBW51P/DynamoDB-Series-Object-Persistence-Model) 
+  [DynamoDB 系列 - 表达式](http://blogs.aws.amazon.com/net/post/TxZQM7VA9AUZ9L/DynamoDB-Series-Expressions) 
+  [在 Amazon DynamoDB 上使用表达式和 适用于 .NET 的 AWS SDK](dynamodb-expressions.md) 
+  [Amazon DynamoDB 中的 JSON 支持](dynamodb-json.md) 

 **低级模型、信息和示例** 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 处理表](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetWorkingWithTables.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 处理项目](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 查询表](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetQuerying.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 扫描表格](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetScanning.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 处理本地二级索引](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSILowLevelDotNet.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 使用全局二级索引](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSILowLevelDotNet.html) 

 **文档模型、信息和示例** 
+  [DynamoDB 数据类型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html#DataModel.DataTypes) 
+  [迪纳摩 DBEntry](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TDynamoDBv2DocumentModelDynamoDBEntry.html) 
+  [.NET：文档模型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKMidLevel.html) 

 **对象持久性模型、信息和示例** 
+  [.NET：对象持久化模型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html) 

 **其他有用信息** 
+ 有关通过 .NET Aspire 使用 Amazon DynamoDB local 进行开发的信息，请参阅[将 AWS 与 .NET Aspire 集成](aspire-integrations.md)。

**Topics**
+ [低级模型](#dynamodb-intro-apis-low-level)
+ [文档模型](#dynamodb-intro-apis-document)
+ [对象持久化模型](#dynamodb-intro-apis-object-persistence)
+ [更多信息](#dynamodb-intro-more-info)
+ [使用表达式](dynamodb-expressions.md)
+ [JSON 支持](dynamodb-json.md)

# 在 Amazon DynamoDB 上使用表达式和 适用于 .NET 的 AWS SDK
<a name="dynamodb-expressions"></a>

**注意**  
本主题中的信息特定于基于.NET Framework 和 3.3 及更早 适用于 .NET 的 AWS SDK 版本的项目。

以下代码示例演示如何使用通过表达式对 DynamoDB 适用于 .NET 的 AWS SDK 进行编程。*表达式*表示您希望从 DynamoDB 表中的某个项目读取到的属性。您还可以在编写项目时使用表达式指示任意必需满足的条件（也称为*有条件更新*），以及需要如何更新属性。一些更新示例使用新值替换属性，或者将新数据添加到列表或映射。有关更多信息，请参阅[使用表达式读取和写入项目](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html)。

**Topics**
+ [示例数据](#dynamodb-expressions-sample-data)
+ [使用表达式和项目的主键获取单个项目](#dynamodb-expressions-get-item)
+ [使用表达式和表主键获取多个项目](#dynamodb-expressions-query)
+ [使用表达式和其他项目属性获取多个项目](#dynamodb-expressions-scan)
+ [输出项目](#dynamodb-expressions-print-item)
+ [使用表达式创建或替换项目](#dynamodb-expressions-put-item)
+ [使用表达式更新项目](#dynamodb-expressions-update-item)
+ [使用表达式删除项目](#dynamodb-expressions-delete-item)
+ [更多信息](#dynamodb-expressions-resources)

## 示例数据
<a name="dynamodb-expressions-sample-data"></a>

此主题中的代码示例依赖于名为 `ProductCatalog` 的 DynamoDB 表中的以下两个示例项目。这些项目描述一家虚构自行车商店目录中产品条目的相关信息。这些项目基于[案例研究：A Ite ProductCatalog m](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.CaseStudy.html) 中提供的示例。`BOOL`、`L`、`M`、`N`、`NS`、`S` 和 `SS` 等数据类型描述符对应于 [JSON 数据格式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataFormat.html)中的那些描述符。

```
{
  "Id": {
    "N": "205"
  },
  "Title": {
    "S": "20-Bicycle 205"
  },
  "Description": {
    "S": "205 description"
  },
  "BicycleType": {
    "S": "Hybrid"
  },
  "Brand": {
    "S": "Brand-Company C"
  },
  "Price": {
    "N": "500"
  },
  "Gender": {
    "S": "B"
  },
  "Color": {
    "SS": [
      "Red",
      "Black"
    ]
  },
  "ProductCategory": {
    "S": "Bike"
  },
  "InStock": {
    "BOOL": true
  },
  "QuantityOnHand": {
    "N": "1"
  },
  "RelatedItems": {
    "NS": [
      "341",
      "472",
      "649"
    ]
  },
  "Pictures": {
    "L": [
      {
        "M": {
          "FrontView": {
            "S": "http://example/products/205_front.jpg"
          }
        }
      },
      {
        "M": {
          "RearView": {
            "S": "http://example/products/205_rear.jpg"
          }
        }
      },
      {
        "M": {
          "SideView": {
            "S": "http://example/products/205_left_side.jpg"
          }
        }
      }
    ]
  },
  "ProductReviews": {
    "M": {
      "FiveStar": {
        "SS": [
          "Excellent! Can't recommend it highly enough! Buy it!",
          "Do yourself a favor and buy this."
        ]
      },
      "OneStar": {
        "SS": [
          "Terrible product! Do not buy this."
        ]
      }
    }
  }
},
{
  "Id": {
    "N": "301"
  },
  "Title": {
    "S": "18-Bicycle 301"
  },
  "Description": {
    "S": "301 description"
  },
  "BicycleType": {
    "S": "Road"
  },
  "Brand": {
    "S": "Brand-Company C"
  },
  "Price": {
    "N": "185"
  },
  "Gender": {
    "S": "F"
  },
  "Color": {
    "SS": [
      "Blue",
      "Silver"
    ]
  },
  "ProductCategory": {
    "S": "Bike"
  },
  "InStock": {
    "BOOL": true
  },
  "QuantityOnHand": {
    "N": "3"
  },
  "RelatedItems": {
    "NS": [
      "801",
      "822",
      "979"
    ]
  },
  "Pictures": {
    "L": [
      {
        "M": {
          "FrontView": {
            "S": "http://example/products/301_front.jpg"
          }
        }
      },
      {
        "M": {
          "RearView": {
            "S": "http://example/products/301_rear.jpg"
          }
        }
      },
      {
        "M": {
          "SideView": {
            "S": "http://example/products/301_left_side.jpg"
          }
        }
      }
    ]
  },
  "ProductReviews": {
    "M": {
      "FiveStar": {
        "SS": [
          "My daughter really enjoyed this bike!"
        ]
      },
      "ThreeStar": {
        "SS": [
          "This bike was okay, but I would have preferred it in my color.",
	      "Fun to ride."
        ]
      }
    }
  }
}
```

## 使用表达式和项目的主键获取单个项目
<a name="dynamodb-expressions-get-item"></a>

以下示例介绍了 `Amazon.DynamoDBv2.AmazonDynamoDBClient.GetItem` 方法和一组表达式，用于获取并输出 `Id` 为 `205` 的项目。只返回项目的以下属性：`Id`、`Title`、`Description`、`Color`、`RelatedItems`、`Pictures` 和 `ProductReviews`。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new GetItemRequest
{
  TableName = "ProductCatalog",
  ProjectionExpression = "Id, Title, Description, Color, #ri, Pictures, #pr",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#pr", "ProductReviews" },
    { "#ri", "RelatedItems" }
  },
  Key = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "205" } }
  },
};
var response = client.GetItem(request);

// PrintItem() is a custom function.
PrintItem(response.Item);
```

在上述示例中，`ProjectionExpression` 属性指定要返回的属性。`ExpressionAttributeNames` 属性指定占位符 `#pr` (表示 `ProductReviews` 属性) 和占位符 `#ri` (表示 `RelatedItems` 属性)。对 `PrintItem` 的调用引用自定义函数，如[输出项目](#dynamodb-expressions-print-item)中所述。

## 使用表达式和表主键获取多个项目
<a name="dynamodb-expressions-query"></a>

以下示例介绍了 `Amazon.DynamoDBv2.AmazonDynamoDBClient.Query` 方法和一组表达式，仅在 `Id` 的值大于 `301` 时，获取并输出 `Price` 为 `150` 的项目。只返回项目的以下属性：`Id`、`Title` 以及所有 `ThreeStar` 中的所有 `ProductReviews` 属性。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new QueryRequest
{
  TableName = "ProductCatalog",
  KeyConditions = new Dictionary<string,Condition>
  {
    { "Id", new Condition()
      {
        ComparisonOperator = ComparisonOperator.EQ,
        AttributeValueList = new List<AttributeValue>
        {
          new AttributeValue { N = "301" }
        }
      }
    }
  },
  ProjectionExpression = "Id, Title, #pr.ThreeStar",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#pr", "ProductReviews" },
    { "#p", "Price" }
  },
  ExpressionAttributeValues = new Dictionary<string,AttributeValue>
  {
    { ":val", new AttributeValue { N = "150" } }
  },
  FilterExpression = "#p > :val"
};
var response = client.Query(request);

foreach (var item in response.Items)
{
  // Write out the first page of an item's attribute keys and values.
  // PrintItem() is a custom function.
  PrintItem(item);
  Console.WriteLine("=====");
}
```

在上述示例中，`ProjectionExpression` 属性指定要返回的属性。`ExpressionAttributeNames` 属性指定占位符 `#pr`（表示 `ProductReviews` 属性）和占位符 `#p`（表示 `Price` 属性）。`#pr.ThreeStar` 指定只返回 `ThreeStar` 属性。`ExpressionAttributeValues` 属性指定占位符 `:val` 来表示值 `150`。`FilterExpression` 属性指定 `#p` (`Price`) 必须大于 `:val` (`150`)。对 `PrintItem` 的调用引用自定义函数，如[输出项目](#dynamodb-expressions-print-item)中所述。

## 使用表达式和其他项目属性获取多个项目
<a name="dynamodb-expressions-scan"></a>

以下示例介绍了 `Amazon.DynamoDBv2.AmazonDynamoDBClient.Scan` 方法和一组表达式，用于获取并输出 `ProductCategory` 为 `Bike` 的所有项目。只返回项目的以下属性：`Id`、`Title` 以及 `ProductReviews` 中的所有属性。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new ScanRequest
{
  TableName = "ProductCatalog",
  ProjectionExpression = "Id, Title, #pr",
  ExpressionAttributeValues = new Dictionary<string,AttributeValue>
  {
    { ":catg", new AttributeValue { S = "Bike" } }
  },
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#pr", "ProductReviews" },
    { "#pc", "ProductCategory" }
  },
  FilterExpression = "#pc = :catg",  
};
var response = client.Scan(request);

foreach (var item in response.Items)
{
  // Write out the first page/scan of an item's attribute keys and values.
  // PrintItem() is a custom function.
  PrintItem(item);
  Console.WriteLine("=====");
}
```

在上述示例中，`ProjectionExpression` 属性指定要返回的属性。`ExpressionAttributeNames` 属性指定占位符 `#pr` (表示 `ProductReviews` 属性) 和占位符 `#pc` (表示 `ProductCategory` 属性)。`ExpressionAttributeValues` 属性指定占位符 `:catg` 来表示值 `Bike`。`FilterExpression` 属性指定 `#pc` (`ProductCategory`) 必须等于 `:catg` (`Bike`)。对 `PrintItem` 的调用引用自定义函数，如[输出项目](#dynamodb-expressions-print-item)中所述。

## 输出项目
<a name="dynamodb-expressions-print-item"></a>

以下示例演示如何输出项目的属性和值。此示例已在前面的几个示例中使用过，这些示例演示如何[使用表达式和项目的主键获取单个项目](#dynamodb-expressions-get-item)、[使用表达式和表主键获取多个项目](#dynamodb-expressions-query)以及[使用表达式和其他项目属性获取多个项目](#dynamodb-expressions-scan)。

```
// using Amazon.DynamoDBv2.Model;

// Writes out an item's attribute keys and values.
public static void PrintItem(Dictionary<string, AttributeValue> attrs)
{
  foreach (KeyValuePair<string, AttributeValue> kvp in attrs)
  {
    Console.Write(kvp.Key + " = ");
    PrintValue(kvp.Value);
  }
}

// Writes out just an attribute's value.
public static void PrintValue(AttributeValue value)
{
  // Binary attribute value.
  if (value.B != null)
  {
    Console.Write("Binary data");
  }
  // Binary set attribute value.
  else if (value.BS.Count > 0)
  {
    foreach (var bValue in value.BS)
    {
      Console.Write("\n  Binary data");
    }
  }
  // List attribute value.
  else if (value.L.Count > 0)
  {
    foreach (AttributeValue attr in value.L)
    {
      PrintValue(attr);
    }
  }
  // Map attribute value.
  else if (value.M.Count > 0)
  {
    Console.Write("\n");
    PrintItem(value.M);
  }
  // Number attribute value.
  else if (value.N != null)
  {
    Console.Write(value.N);
  }
  // Number set attribute value.
  else if (value.NS.Count > 0)
  {
    Console.Write("{0}", string.Join("\n", value.NS.ToArray()));
  }
  // Null attribute value.
  else if (value.NULL)
  {
    Console.Write("Null");
  }
  // String attribute value.
  else if (value.S != null)
  {
    Console.Write(value.S);
  }
  // String set attribute value.
  else if (value.SS.Count > 0)
  {
    Console.Write("{0}", string.Join("\n", value.SS.ToArray()));
  }
  // Otherwise, boolean value.
  else
  {
    Console.Write(value.BOOL);
  }
 
  Console.Write("\n");
}
```

在前面的示例中，每个属性值都有多个 data-type-specific属性，可以通过评估这些属性来确定打印属性的正确格式。这些属性包括 `B`、`BOOL`、`BS`、`L`、`M`、`N`、`NS`、`NULL`、`S` 和 `SS`，对应于 [JSON 数据格式](DataFormat.html)中的那些属性。对于 `B`、`N`、`NULL` 和 `S` 等属性，如果对应属性不是 `null`，则属性是对应的非 `null` 数据类型。对于诸如`BS`、、`L`、`M``NS``SS`、和之类的属性，如果`Count`大于零，则该属性属于相应 non-zero-value的数据类型。如果该属性的所有属性均为`null`或`Count`等于零，则该属性对应于`BOOL`数据类型。 data-type-specific

## 使用表达式创建或替换项目
<a name="dynamodb-expressions-put-item"></a>

以下示例介绍了 `Amazon.DynamoDBv2.AmazonDynamoDBClient.PutItem` 方法和一组表达式，用于更新 `Title` 为 `18-Bicycle 301` 的项目。如果项目不存在，将添加新项目。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new PutItemRequest
{
  TableName = "ProductCatalog",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#title", "Title" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
    { ":product", new AttributeValue { S = "18-Bicycle 301" } }
  },
  ConditionExpression = "#title = :product", 
  // CreateItemData() is a custom function.
  Item = CreateItemData()
};
client.PutItem(request);
```

在上述示例中，`ExpressionAttributeNames` 属性指定占位符 `#title`，以表示 `Title` 属性。`ExpressionAttributeValues` 属性指定占位符 `:product` 来表示值 `18-Bicycle 301`。`ConditionExpression` 属性指定 `#title` (`Title`) 必须等于 `:product` (`18-Bicycle 301`)。对 `CreateItemData` 的调用引用以下自定义函数：

```
// using Amazon.DynamoDBv2.Model;

// Provides a sample item that can be added to a table.
public static Dictionary<string, AttributeValue> CreateItemData()
{
  var itemData = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "301" } },
    { "Title", new AttributeValue { S = "18\" Girl's Bike" } },
    { "BicycleType", new AttributeValue { S = "Road" } },
    { "Brand" , new AttributeValue { S = "Brand-Company C" } },
    { "Color", new AttributeValue { SS = new List<string>{ "Blue", "Silver" } } },
    { "Description", new AttributeValue { S = "301 description" } },
    { "Gender", new AttributeValue { S = "F" } },
    { "InStock", new AttributeValue { BOOL = true } },
    { "Pictures", new AttributeValue { L = new List<AttributeValue>{ 
      { new AttributeValue { M = new Dictionary<string,AttributeValue>{
        { "FrontView", new AttributeValue { S = "http://example/products/301_front.jpg" } } } } },
      { new AttributeValue { M = new Dictionary<string,AttributeValue>{
        { "RearView", new AttributeValue {S = "http://example/products/301_rear.jpg" } } } } },
      { new AttributeValue { M = new Dictionary<string,AttributeValue>{
        { "SideView", new AttributeValue { S = "http://example/products/301_left_side.jpg" } } } } }
    } } },
    { "Price", new AttributeValue { N = "185" } },
    { "ProductCategory", new AttributeValue { S = "Bike" } },
    { "ProductReviews", new AttributeValue { M = new Dictionary<string,AttributeValue>{
      { "FiveStar", new AttributeValue { SS = new List<string>{
        "My daughter really enjoyed this bike!" } } },
      { "OneStar", new AttributeValue { SS = new List<string>{
        "Fun to ride.",
        "This bike was okay, but I would have preferred it in my color." } } }
    } } },
    { "QuantityOnHand", new AttributeValue { N = "3" } },
    { "RelatedItems", new AttributeValue { NS = new List<string>{ "979", "822", "801" } } }
  };

  return itemData;
}
```

在上述示例中，具有示例数据的示例项目返回到调用方。构建了一系列属性和对应值，使用 `BOOL`、`L`、`M`、`N`、`NS`、`S` 和 `SS` 等数据类型，它们对应于 [JSON 数据格式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataFormat.html)中的那些类型。

## 使用表达式更新项目
<a name="dynamodb-expressions-update-item"></a>

以下示例介绍了 `Amazon.DynamoDBv2.AmazonDynamoDBClient.UpdateItem` 方法和一组表达式，用于将 `Title` 为 `18" Girl's Bike` 的项目的 `Id` 更改为 `301`。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new UpdateItemRequest
{
  TableName = "ProductCatalog",
  Key = new Dictionary<string,AttributeValue>
  {
     { "Id", new AttributeValue { N = "301" } }
  },
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#title", "Title" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
    { ":newproduct", new AttributeValue { S = "18\" Girl's Bike" } }
  },
  UpdateExpression = "SET #title = :newproduct"
};
client.UpdateItem(request);
```

在上述示例中，`ExpressionAttributeNames` 属性指定占位符 `#title`，以表示 `Title` 属性。`ExpressionAttributeValues` 属性指定占位符 `:newproduct` 来表示值 `18" Girl's Bike`。`UpdateExpression` 属性指定将 `#title` (`Title`) 更改为 `:newproduct` (`18" Girl's Bike`)。

## 使用表达式删除项目
<a name="dynamodb-expressions-delete-item"></a>

以下示例介绍了 `Amazon.DynamoDBv2.AmazonDynamoDBClient.DeleteItem` 方法和一组表达式，仅在项目的 `Title` 为 `18-Bicycle 301` 时，删除 `Id` 为 `301` 的项目。

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new DeleteItemRequest
{
  TableName = "ProductCatalog",
  Key = new Dictionary<string,AttributeValue>
  {
     { "Id", new AttributeValue { N = "301" } }
  },
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#title", "Title" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
    { ":product", new AttributeValue { S = "18-Bicycle 301" } }
  },
  ConditionExpression = "#title = :product"
};
client.DeleteItem(request);
```

在上述示例中，`ExpressionAttributeNames` 属性指定占位符 `#title`，以表示 `Title` 属性。`ExpressionAttributeValues` 属性指定占位符 `:product` 来表示值 `18-Bicycle 301`。`ConditionExpression` 属性指定 `#title` (`Title`) 必须等于 `:product` (`18-Bicycle 301`)。

## 更多信息
<a name="dynamodb-expressions-resources"></a>

有关更多信息以及代码示例，请参阅：
+  [DynamoDB 系列 - 表达式](http://blogs.aws.amazon.com/net/post/TxZQM7VA9AUZ9L/DynamoDB-Series-Expressions) 
+  [使用投影表达式访问项目属性](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.AccessingItemAttributes.html) 
+  [为属性名称和值使用占位符](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ExpressionPlaceholders.html) 
+  [使用条件表达式指定条件](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html) 
+  [使用更新表达式修改项目和属性](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Modifying.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 处理项目](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 查询表](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetQuerying.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 扫描表格](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetScanning.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 处理本地二级索引](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSILowLevelDotNet.html) 
+  [使用 适用于 .NET 的 AWS SDK 低级 API 使用全局二级索引](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSILowLevelDotNet.html) 

# Amazon DynamoDB 中的 JSON 支持
<a name="dynamodb-json"></a>

**注意**  
本主题中的信息特定于基于.NET Framework 和 3.3 及更早 适用于 .NET 的 AWS SDK 版本的项目。

使用亚马逊 DynamoDB 时 适用于 .NET 的 AWS SDK 支持 JSON 数据。这使您能够更轻松地从 DynamoDB 中获取 JSON 格式的数据以及将 JSON 文档插入到其中。

**Topics**
+ [从 DynamoDB 表以 JSON 格式获取数据](#dynamodb-json-get-table-data)
+ [将 JSON 格式数据插入 DynamoDB 表](#dynamodb-json-insert-table-data)
+ [DynamoDB 数据类型转换为 JSON](#dynamodb-json-datatypes)
+ [更多信息](#dynamodb-json-more-info)

## 从 DynamoDB 表以 JSON 格式获取数据
<a name="dynamodb-json-get-table-data"></a>

以下示例显示了如何以 JSON 格式从 DynamoDB 表中获取数据：

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.DocumentModel;

var client = new AmazonDynamoDBClient();
var table = Table.LoadTable(client, "AnimalsInventory");
var item = table.GetItem(3, "Horse");

var jsonText = item.ToJson();
Console.Write(jsonText);
      
// Output:
//   {"Name":"Shadow","Type":"Horse","Id":3}

var jsonPrettyText = item.ToJsonPretty();
Console.WriteLine(jsonPrettyText);
      
// Output:
//   {
//     "Name" : "Shadow",
//     "Type" : "Horse",
//     "Id"   : 3
//   }
```

在上述示例中，`Document` 类的 `ToJson` 方法将表中的项目转换为 JSON 格式的字符串。项目通过 `Table` 类的 `GetItem` 方法检索。为了确定要获取的项目，在本示例中，该`GetItem`方法使用目标项目 hash-and-range的主键。为确定要从中获取项目的表，`Table` 类的 `LoadTable` 方法使用 `AmazonDynamoDBClient` 类的实例以及 DynamoDB 中的目标表名。

## 将 JSON 格式数据插入 DynamoDB 表
<a name="dynamodb-json-insert-table-data"></a>

以下示例演示如何使用 JSON 格式将项目插入到 DynamoDB 表：

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.DocumentModel;

var client = new AmazonDynamoDBClient();
var table = Table.LoadTable(client, "AnimalsInventory");
var jsonText = "{\"Id\":6,\"Type\":\"Bird\",\"Name\":\"Tweety\"}";
var item = Document.FromJson(jsonText);

table.PutItem(item);
```

在上述示例中，`FromJson` 类的 `Document` 方法将 JSON 格式的字符串转换为项目。项目通过 `PutItem` 类的 `Table` 方法插入表中，该方法使用包含项目的 `Document` 类的实例。为确定要插入项目的表，调用 `Table` 类的 `LoadTable` 方法，并指定 `AmazonDynamoDBClient` 类的实例以及 DynamoDB 中的目标表名。

## DynamoDB 数据类型转换为 JSON
<a name="dynamodb-json-datatypes"></a>

当您调用 `Document` 类的 `ToJson` 方法，然后在生成的 JSON 数据上调用 `FromJson` 方法以将 JSON 数据转换回 `Document` 类的实例时，一些 DynamoDB 数据类型不会按预期转换。具体来说：
+ DynamoDB 集（`SS`、`NS` 和 `BS` 类型）将转换为 JSON 数组。
+ DynamoDB 二进制标量和集（`B` 和 `BS` 类型）将转换为 base64 编码的 JSON 字符串或字符串列表。

  在此情况下，您必须调用 `DecodeBase64Attributes` 类的 `Document` 方法，使用正确的二进制表示形式替换 base64 编码的 JSON 数据。以下示例使用正确的二进制表示形式，在 `Document` 类的实例中，替换 base64 编码的名为 `Picture` 的二进制标量项目属性。此示例还在 `Document` 类的相同实例中，为名为 `RelatedPictures` 的 base64 编码二进制集项目属性执行相同的操作。

  ```
  item.DecodeBase64Attributes("Picture", "RelatedPictures");
  ```

## 更多信息
<a name="dynamodb-json-more-info"></a>

有关使用 DynamoDB 编程 JSON 的更多信息和示例， 适用于 .NET 的 AWS SDK请参阅：
+  [DynamoDB JSON 支持](https://aws.amazon.com/blogs/developer/dynamodb-json-support/) 
+  [Amazon DynamoDB 更新 - JSON、更广泛的免费套餐、灵活扩展、更大的项目](https://aws.amazon.com/blogs/aws/dynamodb-update-json-and-more/) 

# 使用 Amazon EC2
<a name="ec2-apis-intro"></a>

 适用于 .NET 的 AWS SDK 支持 [Amazon EC2](https://docs.aws.amazon.com/ec2/)，这是一项提供可调整计算容量的网络服务。您可以使用这种计算容量来构建和托管您的软件系统。

## APIs
<a name="w2aac19c15c17b5"></a>

为 Amazon EC2 客户 适用于 .NET 的 AWS SDK 提供了 API。这些 API 使您能够使用安全组和密钥对等 EC2 功能。API 还使您能够控制 Amazon EC2 实例。本节包含少量示例，向您展示使用这些示例时可以遵循的模式 APIs。要查看全套内容 APIs，请参阅 [适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)（并滚动至 “Amazon.ec2”）。

亚马逊 EC2 APIs 由 [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2) NuGet 软件包提供。

## 先决条件
<a name="w2aac19c15c17b7"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

## 关于示例
<a name="ec2-apis-intro-about"></a>

本节中的示例向您展示如何使用 Amazon EC2 客户端和管理 Amazon EC2 实例。

[EC2 竞价型实例教程](how-to-spot-instances.md)向您演示如何请求 Amazon EC2 竞价型实例。竞价型实例允许您以低于按需价格访问未使用的 EC2 容量。

**Topics**
+ [APIs](#w2aac19c15c17b5)
+ [先决条件](#w2aac19c15c17b7)
+ [关于示例](#ec2-apis-intro-about)
+ [安全组](security-groups.md)
+ [密钥对](key-pairs.md)
+ [区域和可用区](using-regions-and-availability-zones.md)
+ [EC2 实例](how-to-ec2.md)
+ [竞价型实例教程](how-to-spot-instances.md)

# 使用 Amazon EC2 中的安全组
<a name="security-groups"></a>

在 Amazon EC2 中，*安全组*起着虚拟防火墙的作用，可控制一个或多个 EC2 实例的网络流量。默认情况下，EC2 将您的实例与不允许入站流量的安全组关联。可以创建允许您的 EC2 实例接受特定流量的安全组。例如，如果您需要连接到 EC2 Windows 实例，就必须配置安全组以允许 RDP 流量。

有关安全组的更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的 [Amazon EC2 安全组](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)。

**警告**  
EC2-Classic 已于 2022 年 8 月 15 日停用。我们建议您从 EC2-Classic 迁移到 VPC。有关更多信息，请参阅博客文章 [EC2-Classic Networking is Retiring - Here's How to Prepare](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)。

有关 APIs 和先决条件的信息，请参阅父部分 ([使用 Amazon EC2](ec2-apis-intro.md))。

**Topics**
+ [枚举安全组](enumerate-security-groups.md)
+ [创建安全组](creating-security-group.md)
+ [更新安全组](authorize-ingress.md)

# 枚举安全组
<a name="enumerate-security-groups"></a>

此示例向您展示如何使用枚举安全组。 适用于 .NET 的 AWS SDK 如果您提供 [Amazon Virtual Private Cloud](https://docs.aws.amazon.com/vpc/latest/userguide/) ID，则应用程序会枚举该特定 VPC 的安全组。否则，应用程序仅显示所有可用安全组的列表。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#enum-sec-groups-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [枚举安全组](#enum-sec-groups-enum)
+ [完整代码](#enum-sec-groups-complete-code)
+ [其他注意事项](#enum-sec-groups-additional)

## 枚举安全组
<a name="enum-sec-groups-enum"></a>

以下代码片段枚举了您的安全组。它枚举了特定 VPC（如果给出）的所有组或组。

[本主题末尾](#enum-sec-groups-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to enumerate the security groups
    private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
    {
      // A request object, in case we need it.
      var request = new DescribeSecurityGroupsRequest();

      // Put together the properties, if needed
      if(!string.IsNullOrEmpty(vpcID))
      {
        // We have a VPC ID. Find the security groups for just that VPC.
        Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
        request.Filters.Add(new Filter
        {
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });
      }

      // Get the list of security groups
      DescribeSecurityGroupsResponse response =
        await ec2Client.DescribeSecurityGroupsAsync(request);

      // Display the list of security groups.
      foreach (SecurityGroup item in response.SecurityGroups)
      {
        Console.WriteLine("Security group: " + item.GroupId);
        Console.WriteLine("\tGroupId: " + item.GroupId);
        Console.WriteLine("\tGroupName: " + item.GroupName);
        Console.WriteLine("\tVpcId: " + item.VpcId);
        Console.WriteLine();
      }
    }
```

## 完整代码
<a name="enum-sec-groups-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c17c13c13c15b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)

  班级 [DescribeSecurityGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsResponse.html)

  类 [Filter](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TFilter.html)

  班级 [SecurityGroup](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSecurityGroup.html)

### 代码
<a name="w2aac19c15c17c13c13c15b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2EnumerateSecGroups
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line
       string vpcID = string.Empty;
      if(args.Length == 0)
      {
        Console.WriteLine("\nEC2EnumerateSecGroups [vpc_id]");
        Console.WriteLine("  vpc_id - The ID of the VPC for which you want to see security groups.");
        Console.WriteLine("\nSince you specified no arguments, showing all available security groups.");
      }
      else
      {
        vpcID = args[0];
      }

      if(vpcID.StartsWith("vpc-") || string.IsNullOrEmpty(vpcID))
      {
        // Create an EC2 client object
        var ec2Client = new AmazonEC2Client();

        // Enumerate the security groups
        await EnumerateGroups(ec2Client, vpcID);
      }
      else
      {
        Console.WriteLine("Could not find a valid VPC ID in the command-line arguments:");
        Console.WriteLine($"{args[0]}");
      }
    }


    //
    // Method to enumerate the security groups
    private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
    {
      // A request object, in case we need it.
      var request = new DescribeSecurityGroupsRequest();

      // Put together the properties, if needed
      if(!string.IsNullOrEmpty(vpcID))
      {
        // We have a VPC ID. Find the security groups for just that VPC.
        Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
        request.Filters.Add(new Filter
        {
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });
      }

      // Get the list of security groups
      DescribeSecurityGroupsResponse response =
        await ec2Client.DescribeSecurityGroupsAsync(request);

      // Display the list of security groups.
      foreach (SecurityGroup item in response.SecurityGroups)
      {
        Console.WriteLine("Security group: " + item.GroupId);
        Console.WriteLine("\tGroupId: " + item.GroupId);
        Console.WriteLine("\tGroupName: " + item.GroupName);
        Console.WriteLine("\tVpcId: " + item.VpcId);
        Console.WriteLine();
      }
    }
  }
}
```

## 其他注意事项
<a name="enum-sec-groups-additional"></a>
+ 请注意 VPC 情况，筛选器是通过将名称值对的 `Name` 部分设置为“vpc-id”而构造的。这个名字来自对[DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)类`Filters`属性的描述。
+ 要获取安全组的完整列表，也可以[不 DescribeSecurityGroupsAsync 带任何参数](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/MEC2DescribeSecurityGroupsAsyncCancellationToken.html)使用。
+ 您可以通过在 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)中查看安全组列表来验证结果。

# 创建安全组
<a name="creating-security-group"></a>

此示例向您展示如何使用创建安全组。 适用于 .NET 的 AWS SDK 您可以提供现有 VPC 的 ID，以便在 VPC 中为 EC2 创建安全组。如果您不提供这样的 ID，则新的安全组将适用于 EC2-Classic（前提是您的 AWS 账户支持）。

如果您未提供 VPC ID 且您的 AWS 账户不支持 EC2-Classic，则新的安全组将属于您账户的默认 VPC。

**警告**  
EC2-Classic 已于 2022 年 8 月 15 日停用。我们建议您从 EC2-Classic 迁移到 VPC。有关更多信息，请参阅博客文章 [EC2-Classic Networking is Retiring – Here's How to Prepare](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#create-sec-groups-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [查找现有安全组](#create-sec-groups-find)
+ [创建安全组](#create-sec-groups-enum)
+ [完整代码](#create-sec-groups-complete-code)

## 查找现有安全组
<a name="create-sec-groups-find"></a>

以下代码片段搜索给定 VPC 中具有给定名称的现有安全组。

[本主题末尾](#create-sec-groups-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to determine if a security group with the specified name
    // already exists in the VPC
    private static async Task<List<SecurityGroup>> FindSecurityGroups(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      var request = new DescribeSecurityGroupsRequest();
      request.Filters.Add(new Filter{
        Name = "group-name",
        Values = new List<string>() { groupName }
      });
      if(!string.IsNullOrEmpty(vpcID))
        request.Filters.Add(new Filter{
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });

      var response = await ec2Client.DescribeSecurityGroupsAsync(request);
      return response.SecurityGroups;
    }
```

## 创建安全组
<a name="create-sec-groups-enum"></a>

如果给定的 VPC 中不存在具有该名称的组，则以下片段将创建一个新的安全组。如果未给出 VPC，并且存在一个或多个具有该名称的组，则该片段仅返回组列表。

[本主题末尾](#create-sec-groups-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to create a new security group (either EC2-Classic or EC2-VPC)
    // If vpcID is empty, the security group will be for EC2-Classic
    private static async Task<List<SecurityGroup>> CreateSecurityGroup(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      // See if one or more security groups with that name
      // already exist in the given VPC. If so, return the list of them.
      var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
      if (securityGroups.Count > 0)
      {
        Console.WriteLine(
          $"\nOne or more security groups with name {groupName} already exist.\n");
        return securityGroups;
      }

      // If the security group doesn't already exists, create it.
      var createRequest = new CreateSecurityGroupRequest{
        GroupName = groupName
      };
      if(string.IsNullOrEmpty(vpcID))
      {
        createRequest.Description = "My .NET example security group for EC2-Classic";
      }
      else
      {
        createRequest.VpcId = vpcID;
        createRequest.Description = "My .NET example security group for EC2-VPC";
      }
      CreateSecurityGroupResponse createResponse =
        await ec2Client.CreateSecurityGroupAsync(createRequest);

      // Return the new security group
      DescribeSecurityGroupsResponse describeResponse =
        await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
          GroupIds = new List<string>() { createResponse.GroupId }
        });
      return describeResponse.SecurityGroups;
    }
```

## 完整代码
<a name="create-sec-groups-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c17c13c15c23b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [CreateSecurityGroupRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateSecurityGroupRequest.html)

  班级 [CreateSecurityGroupResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateSecurityGroupResponse.html)

  班级 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)

  班级 [DescribeSecurityGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsResponse.html)

  类 [Filter](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TFilter.html)

  班级 [SecurityGroup](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSecurityGroup.html)

### 代码
<a name="w2aac19c15c17c13c15c23b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2CreateSecGroup
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create a security group
  class Program
  {
    private const int MaxArgs = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }
      if(parsedArgs.Count > MaxArgs)
        CommandLine.ErrorExit("\nThe number of command-line arguments is incorrect." +
          "\nRun the command with no arguments to see help.");

      // Get the application arguments from the parsed list
      var groupName = CommandLine.GetArgument(parsedArgs, null, "-g", "--group-name");
      var vpcID = CommandLine.GetArgument(parsedArgs, null, "-v", "--vpc-id");
      if(string.IsNullOrEmpty(groupName))
        CommandLine.ErrorExit("\nYou must supply a name for the new group." +
          "\nRun the command with no arguments to see help.");
      if(!string.IsNullOrEmpty(vpcID) && !vpcID.StartsWith("vpc-"))
        CommandLine.ErrorExit($"\nNot a valid VPC ID: {vpcID}");

      // groupName has a value and vpcID either has a value or is null (which is fine)
      // Create the new security group and display information about it
      var securityGroups =
        await CreateSecurityGroup(new AmazonEC2Client(), groupName, vpcID);
      Console.WriteLine("Information about the security group(s):");
      foreach(var group in securityGroups)
      {
        Console.WriteLine($"\nGroupName: {group.GroupName}");
        Console.WriteLine($"GroupId: {group.GroupId}");
        Console.WriteLine($"Description: {group.Description}");
        Console.WriteLine($"VpcId (if any): {group.VpcId}");
      }
    }


    //
    // Method to create a new security group (either EC2-Classic or EC2-VPC)
    // If vpcID is empty, the security group will be for EC2-Classic
    private static async Task<List<SecurityGroup>> CreateSecurityGroup(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      // See if one or more security groups with that name
      // already exist in the given VPC. If so, return the list of them.
      var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
      if (securityGroups.Count > 0)
      {
        Console.WriteLine(
          $"\nOne or more security groups with name {groupName} already exist.\n");
        return securityGroups;
      }

      // If the security group doesn't already exists, create it.
      var createRequest = new CreateSecurityGroupRequest{
        GroupName = groupName
      };
      if(string.IsNullOrEmpty(vpcID))
      {
        createRequest.Description = "Security group for .NET code example (no VPC specified)";
      }
      else
      {
        createRequest.VpcId = vpcID;
        createRequest.Description = "Security group for .NET code example (VPC: " + vpcID + ")";
      }
      CreateSecurityGroupResponse createResponse =
        await ec2Client.CreateSecurityGroupAsync(createRequest);

      // Return the new security group
      DescribeSecurityGroupsResponse describeResponse =
        await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
          GroupIds = new List<string>() { createResponse.GroupId }
        });
      return describeResponse.SecurityGroups;
    }


    //
    // Method to determine if a security group with the specified name
    // already exists in the VPC
    private static async Task<List<SecurityGroup>> FindSecurityGroups(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      var request = new DescribeSecurityGroupsRequest();
      request.Filters.Add(new Filter{
        Name = "group-name",
        Values = new List<string>() { groupName }
      });
      if(!string.IsNullOrEmpty(vpcID))
        request.Filters.Add(new Filter{
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });

      var response = await ec2Client.DescribeSecurityGroupsAsync(request);
      return response.SecurityGroups;
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2CreateSecGroup -g <group-name> [-v <vpc-id>]" +
        "\n  -g, --group-name: The name you would like the new security group to have." +
        "\n  -v, --vpc-id: The ID of a VPC to which the new security group will belong." +
        "\n     If vpc-id isn't present, the security group will be" +
        "\n     for EC2-Classic (if your AWS account supports this)" +
        "\n     or will use the default VCP for EC2-VPC.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

# 更新安全组
<a name="authorize-ingress"></a>

此示例向您展示如何使用 适用于 .NET 的 AWS SDK 向安全组添加规则。特别是，该示例添加了一条规则，允许给定 TCP 端口上的入站流量，例如，该端口可用于到 EC2 实例的远程连接。应用程序获取现有安全组的 ID、CIDR 格式的 IP 地址（或地址范围）以及可选的 TCP 端口号。然后，它向给定的安全组添加入站规则。

**注意**  
要使用此示例，您需要一个 CIDR 格式的 IP 地址（或地址范围）。有关获取本地计算机 IP 地址的方法，请参阅本主题末尾的**其它注意事项**。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#authorize-ingress-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [添加入站规则](#authorize-ingress-add-rule)
+ [完整代码](#authorize-ingress-complete-code)
+ [其他注意事项](#authorize-ingress-additional)

## 添加入站规则
<a name="authorize-ingress-add-rule"></a>

以下片段向安全组添加了针对特定 IP 地址（或范围）和 TCP 端口的入站规则。

[本主题末尾](#authorize-ingress-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method that adds a TCP ingress rule to a security group
    private static async Task AddIngressRule(
      IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
    {
      // Create an object to hold the request information for the rule.
      // It uses an IpPermission object to hold the IP information for the rule.
      var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
        GroupId = groupID};
      ingressRequest.IpPermissions.Add(new IpPermission{
        IpProtocol = "tcp",
        FromPort = port,
        ToPort = port,
        Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
      });

      // Create the inbound rule for the security group
      AuthorizeSecurityGroupIngressResponse responseIngress =
        await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
      Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
      Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
    }
```

## 完整代码
<a name="authorize-ingress-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c17c13c17c17b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [AuthorizeSecurityGroupIngressRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TAuthorizeSecurityGroupIngressRequest.html)

  班级 [AuthorizeSecurityGroupIngressResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TAuthorizeSecurityGroupIngressResponse.html)

  班级 [IpPermission](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIpPermission.html)

  班级 [IpRange](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIpRange.html)

### 代码
<a name="w2aac19c15c17c13c17c17b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2AddRuleForRDP
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to add a rule that allows inbound traffic on TCP a port
  class Program
  {
    private const int DefaultPort = 3389;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      var groupID = CommandLine.GetArgument(parsedArgs, null, "-g", "--group-id");
      var ipAddress = CommandLine.GetArgument(parsedArgs, null, "-i", "--ip-address");
      var portStr = CommandLine.GetArgument(parsedArgs, DefaultPort.ToString(), "-p", "--port");
      if(string.IsNullOrEmpty(ipAddress))
        CommandLine.ErrorExit("\nYou must supply an IP address in CIDR format.");
      if(string.IsNullOrEmpty(groupID) || !groupID.StartsWith("sg-"))
        CommandLine.ErrorExit("\nThe ID for a security group is missing or incorrect.");
      if(int.Parse(portStr) == 0)
        CommandLine.ErrorExit($"\nThe given TCP port number, {portStr}, isn't allowed.");

      // Add a rule to the given security group that allows
      // inbound traffic on a TCP port
      await AddIngressRule(
        new AmazonEC2Client(), groupID, ipAddress, int.Parse(portStr));
    }


    //
    // Method that adds a TCP ingress rule to a security group
    private static async Task AddIngressRule(
      IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
    {
      // Create an object to hold the request information for the rule.
      // It uses an IpPermission object to hold the IP information for the rule.
      var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
        GroupId = groupID};
      ingressRequest.IpPermissions.Add(new IpPermission{
        IpProtocol = "tcp",
        FromPort = port,
        ToPort = port,
        Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
      });

      // Create the inbound rule for the security group
      AuthorizeSecurityGroupIngressResponse responseIngress =
        await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
      Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
      Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2AddRuleForRDP -g <group-id> -i <ip-address> [-p <port>]" +
        "\n  -g, --group-id: The ID of the security group to which you want to add the inbound rule." +
        "\n  -i, --ip-address: An IP address or address range in CIDR format." +
        "\n  -p, --port: The TCP port number. Defaults to 3389.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="authorize-ingress-additional"></a>
+ 如果您不提供端口号，则应用程序默认为端口 3389。这是 Windows RDP 的端口，它使您能够连接到运行 Windows 的 EC2 实例。如果您要启动运行 Linux 的 EC2 实例，请改为使用 TCP 端口 22 (SSH)。
+ 请注意，该示例将 `IpProtocol` 设置为“tcp”。的值`IpProtocol`可以在[IpPermission](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIpPermission.html)类`IpProtocol`属性的描述中找到。
+ 使用此示例时，您可能需要本地计算机的 IP 地址。以下是可以获取地址的一些方式。
  + 如果您的本地计算机（您将从该计算机连接到 EC2 实例）具有静态公有 IP 地址，则可以使用服务来获取该地址。其中一项服务是 [http://checkip.amazonaws.com/](http://checkip.amazonaws.com/)。要了解有关授权入站流量的更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[向安全组添加规则](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule)和[针对不同使用案例的安全组规则](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules-reference.html)。
  + 获取本地计算机 IP 地址的另一种方法是使用 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)。

    选择您的一个安全组，选择**入站规则**选项卡，然后选择**编辑入站规则**。在入站规则中，打开**来源**列中的下拉菜单，然后选择**我的 IP** 以查看 CIDR 格式的本地计算机 IP 地址。请务必**取消**该操作。
+ 您可以通过检查 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)中的安全组列表来验证此示例的结果。

# 使用 Amazon EC2 密钥对
<a name="key-pairs"></a>

Amazon EC2 使用公有密钥密码系统来加密和解密登录信息。公有密钥密码系统使用公有密钥加密数据，然后收件人可以使用私有密钥解密数据。公有和私有密钥被称为密钥对。如果要登录 EC2 实例，则必须在启动时指定密钥对，然后在连接时提供密钥对的私钥。

当您启动 EC2 实例时，您可以为其创建密钥对，或使用在启动其它实例时已使用过的密钥对。有关 Amazon EC2 密钥对的信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[使用 Amazon EC2 密钥对](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html)。

有关 APIs 和先决条件的信息，请参阅父部分 ([使用 Amazon EC2](ec2-apis-intro.md))。

**Topics**
+ [创建和显示密钥对](create-save-key-pair.md)
+ [删除密钥对](delete-key-pairs.md)

# 创建和显示密钥对
<a name="create-save-key-pair"></a>

此示例向您展示如何使用创建密钥对。 适用于 .NET 的 AWS SDK 应用程序使用新密钥对的名称和 PEM 文件的名称（扩展名为“.pem”）。其会创建密钥对，将私钥写入 PEM 文件，然后显示所有可用的密钥对。如果您不提供命令行参数，则应用程序仅显示所有可用的密钥对。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#create-save-key-pair-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [创建密钥对](#create-save-key-pair-create)
+ [显示可用的密钥对](#create-save-key-pair-display)
+ [完整代码](#create-save-key-pair-complete-code)
+ [其他注意事项](#create-save-key-pair-additional)

## 创建密钥对
<a name="create-save-key-pair-create"></a>

以下代码片段创建了一个密钥对，然后将私钥存储到给定的 PEM 文件中。

[本主题末尾](#create-save-key-pair-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to create a key pair and save the key material in a PEM file
    private static async Task CreateKeyPair(
      IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
    {
      // Create the key pair
      CreateKeyPairResponse response =
        await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
          KeyName = keyPairName
        });
      Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");

      // Save the private key in a PEM file
      using (var s = new FileStream(pemFileName, FileMode.Create))
      using (var writer = new StreamWriter(s))
      {
        writer.WriteLine(response.KeyPair.KeyMaterial);
      }
    }
```

## 显示可用的密钥对
<a name="create-save-key-pair-display"></a>

以下代码片段显示了可用密钥对的列表。

[本主题末尾](#create-save-key-pair-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
```

## 完整代码
<a name="create-save-key-pair-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c17c15c11c19b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [CreateKeyPairRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateKeyPairRequest.html)

  班级 [CreateKeyPairResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateKeyPairResponse.html)

  班级 [DescribeKeyPairsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeKeyPairsResponse.html)

  班级 [KeyPairInfo](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TKeyPairInfo.html)

### 代码
<a name="w2aac19c15c17c15c11c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.IO;
using Amazon.EC2;
using Amazon.EC2.Model;
using System.Collections.Generic;

namespace EC2CreateKeyPair
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create and store a key pair
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        // In the case of no command-line arguments,
        // just show help and the existing key pairs
        PrintHelp();
        Console.WriteLine("\nNo arguments specified.");
        Console.Write(
          "Do you want to see a list of the existing key pairs? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await EnumerateKeyPairs(ec2Client);
        return;
      }

      // Get the application arguments from the parsed list
      string keyPairName =
        CommandLine.GetArgument(parsedArgs, null, "-k", "--keypair-name");
      string pemFileName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--pem-filename");
      if(string.IsNullOrEmpty(keyPairName))
        CommandLine.ErrorExit("\nNo key pair name specified." +
          "\nRun the command with no arguments to see help.");
      if(string.IsNullOrEmpty(pemFileName) || !pemFileName.EndsWith(".pem"))
        CommandLine.ErrorExit("\nThe PEM filename is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the key pair
      await CreateKeyPair(ec2Client, keyPairName, pemFileName);
      await EnumerateKeyPairs(ec2Client);
    }


    //
    // Method to create a key pair and save the key material in a PEM file
    private static async Task CreateKeyPair(
      IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
    {
      // Create the key pair
      CreateKeyPairResponse response =
        await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
          KeyName = keyPairName
        });
      Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");

      // Save the private key in a PEM file
      using (var s = new FileStream(pemFileName, FileMode.Create))
      using (var writer = new StreamWriter(s))
      {
        writer.WriteLine(response.KeyPair.KeyMaterial);
      }
    }


    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2CreateKeyPair -k <keypair-name> -p <pem-filename>" +
        "\n  -k, --keypair-name: The name you want to assign to the key pair." +
        "\n  -p, --pem-filename: The name of the PEM file to create, with a \".pem\" extension.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="create-save-key-pair-additional"></a>
+ 运行该示例后，您可以在 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/#KeyPairs)中看到新的密钥对。
+ 创建密钥对时，必须保存返回的私钥，因为以后无法检索私钥。

# 删除密钥对
<a name="delete-key-pairs"></a>

此示例向您展示如何使用删除密钥对。 适用于 .NET 的 AWS SDK 应用程序获取一个密钥对的名称。它会删除密钥对，然后显示所有可用的密钥对。如果您不提供命令行参数，则应用程序仅显示所有可用的密钥对。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#delete-key-pairs-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [删除密钥对](#delete-key-pairs-create)
+ [显示可用的密钥对](#delete-key-pairs-display)
+ [完整代码](#delete-key-pairs-complete-code)

## 删除密钥对
<a name="delete-key-pairs-create"></a>

以下代码片段删除了密钥对。

[本主题末尾](#delete-key-pairs-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to delete a key pair
    private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
    {
      await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
        KeyName = keyName});
      Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
    }
```

## 显示可用的密钥对
<a name="delete-key-pairs-display"></a>

以下代码片段显示了可用密钥对的列表。

[本主题末尾](#delete-key-pairs-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
```

## 完整代码
<a name="delete-key-pairs-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c17c15c13c19b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)

  班级 [DescribeKeyPairsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeKeyPairsResponse.html)

  班级 [KeyPairInfo](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TKeyPairInfo.html)

### 代码
<a name="w2aac19c15c17c15c13c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2DeleteKeyPair
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      if(args.Length == 1)
      {
        // Delete a key pair (if it exists)
        await DeleteKeyPair(ec2Client, args[0]);

        // Display the key pairs that are left
        await EnumerateKeyPairs(ec2Client);
      }
      else
      {
        Console.WriteLine("\nUsage: EC2DeleteKeyPair keypair-name");
        Console.WriteLine("  keypair-name - The name of the key pair you want to delete.");
        Console.WriteLine("\nNo arguments specified.");
        Console.Write(
          "Do you want to see a list of the existing key pairs? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await EnumerateKeyPairs(ec2Client);
      }
    }


    //
    // Method to delete a key pair
    private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
    {
      await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
        KeyName = keyName});
      Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
    }


    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
  }
}
```

# 设置 Amazon EC2 区域和可用区
<a name="using-regions-and-availability-zones"></a>

Amazon EC2 托管在全球多个位置。这些位置由 区域和可用区构成。每个区域都是一个独立的地理区域，具有多个相互隔离的位置，这些位置称为可用区。

有关区域和可用区的更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[区域和可用区](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html)。

此示例向您展示如何使用获取与 EC2 客户端相关的区域和可用区的详细信息。 适用于 .NET 的 AWS SDK 该应用程序显示 EC2 客户端可用的区域和可用区列表。

## SDK 参考
<a name="w2aac19c15c17c17b9b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [DescribeAvailabilityZonesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeAvailabilityZonesResponse.html)

  班级 [DescribeRegionsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeRegionsResponse.html)

  班级 [AvailabilityZone](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TAvailabilityZone.html)

  类 [Region](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRegion.html)

```
using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2RegionsAndZones
{
  class Program
  {
    static async Task Main(string[] args)
    {
      Console.WriteLine(
        "Finding the Regions and Availability Zones available to an EC2 client...");

      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Display the Regions and Availability Zones
      await DescribeRegions(ec2Client);
      await DescribeAvailabilityZones(ec2Client);
    }


    //
    // Method to display Regions
    private static async Task DescribeRegions(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nRegions that are enabled for the EC2 client:");
      DescribeRegionsResponse response = await ec2Client.DescribeRegionsAsync();
      foreach (Region region in response.Regions)
        Console.WriteLine(region.RegionName);
    }


    //
    // Method to display Availability Zones
    private static async Task DescribeAvailabilityZones(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nAvailability Zones for the EC2 client's region:");
      DescribeAvailabilityZonesResponse response =
        await ec2Client.DescribeAvailabilityZonesAsync();
      foreach (AvailabilityZone az in response.AvailabilityZones)
        Console.WriteLine(az.ZoneName);
    }
  }
}
```

# 使用 Amazon EC2 实例
<a name="how-to-ec2"></a>

您可以使用通过创建、启动和终止等操作 适用于 .NET 的 AWS SDK 来控制 Amazon EC2 实例。本节中的主题提供了一些如何执行此操作的示例。要了解有关 EC2 实例的更多信息，请参阅[亚马逊 EC2 [用户指南中的亚马逊 EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/) 实](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Instances.html)例。

有关 APIs 和先决条件的信息，请参阅父部分 ([使用 Amazon EC2](ec2-apis-intro.md))。

**Topics**
+ [启动 EC2 实例](run-instance.md)
+ [终止 EC2 实例](terminate-instance.md)

# 启动 Amazon EC2 实例
<a name="run-instance"></a>

此示例向您展示如何使用从同一个亚马逊系统映像 (AMI) 启动一个或多个配置相同的 Amazon EC2 实例。 适用于 .NET 的 AWS SDK 应用程序使用您提供的[多个输入](#run-instance-gather)启动一个 EC2 实例，然后监控该实例，直到其退出“待处理”状态。

当您的 EC2 实例运行时，您可以远程连接到该实例，如[（可选）连接到实例](#connect-to-instance)中所述。

**警告**  
EC2-Classic 已于 2022 年 8 月 15 日停用。我们建议您从 EC2-Classic 迁移到 VPC。有关更多信息，请参阅博客文章 [EC2-Classic Networking is Retiring – Here's How to Prepare](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)。

以下各节提供了此示例的片段和其它信息。片段后显示了[该示例的完整代码](#run-instance-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [收集所需内容](#run-instance-gather)
+ [启动实例](#run-instance-launch)
+ [监控实例](#run-instance-monitor)
+ [完整代码](#run-instance-complete-code)
+ [其他注意事项](#run-instance-additional)
+ [（可选）连接到实例](#connect-to-instance)
+ [清理](#run-instance-cleanup)

## 收集所需内容
<a name="run-instance-gather"></a>

要启动 EC2 实例，您需要具备多项条件。
+ 将在其中启动实例的 [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/)。如果它是 Windows 实例，并且您将通过 RDP 连接到该实例，那么 VPC 很可能需要连接互联网网关，并在路由表中添加互联网网关的条目。有关更多信息，请参阅《*Amazon VPC 用户指南*》中的[互联网网关](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html)。
+ 将在其中启动实例的 VPC 现有子网的 ID。要查找或创建它，一种简单的方法是登录 [Amazon VPC 控制台](https://console.aws.amazon.com/vpc/home#subnets)，但您也可以使用[CreateSubnetAsync](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/MEC2CreateSubnetAsyncCreateSubnetRequestCancellationToken.html)和[DescribeSubnetsAsync](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/MEC2DescribeSubnetsAsyncDescribeSubnetsRequestCancellationToken.html)方法以编程方式获取它。
**注意**  
如果您没有提供此参数，则新实例将在您账户的默认 VPC 中启动。
+ 属于将在其中启动实例的 VPC 的现有安全组的 ID。有关更多信息，请参阅 [使用 Amazon EC2 中的安全组](security-groups.md)。
+ 如果要连接到新实例，则前面提到的安全组必须有相应的入站规则，允许端口 22（Linux 实例）上的 SSH 流量或端口 3389（Windows 实例）上的 RDP 流量。有关如何执行此操作的信息，请参阅[更新安全组](authorize-ingress.md)，包括该主题接近末尾处的[其他注意事项](authorize-ingress.md#authorize-ingress-additional)。
+ 要用于创建实例的自定义亚马逊机器映像（AMI）的 ID。有关信息 AMIs，请参阅 [Amazon [EC2 用户指南中的亚马逊系统](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)映像 (AMIs)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)。具体而言，请参阅[查找 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html) 并[共享 AMIs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)。
+ 现有 EC2 密钥对的名称，用于连接到新实例。有关更多信息，请参阅 [使用 Amazon EC2 密钥对](key-pairs.md)。
+ 包含前面提到的 EC2 密钥对私钥的 PEM 文件的名称。当您[远程连接到](#connect-to-instance)实例时，将使用 PEM 文件。

## 启动实例
<a name="run-instance-launch"></a>

以下代码片段启动 EC2 实例。

[本主题接近末尾处](#run-instance-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to launch the instances
    // Returns a list with the launched instance IDs
    private static async Task<List<string>> LaunchInstances(
      IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
    {
      var instanceIds = new List<string>();
      RunInstancesResponse responseLaunch =
        await ec2Client.RunInstancesAsync(requestLaunch);

      Console.WriteLine("\nNew instances have been created.");
      foreach (Instance item in responseLaunch.Reservation.Instances)
      {
        instanceIds.Add(item.InstanceId);
        Console.WriteLine($"  New instance: {item.InstanceId}");
      }

      return instanceIds;
    }
```

## 监控实例
<a name="run-instance-monitor"></a>

以下代码片段会监控该实例，直到其退出“待处理”状态。

[本主题接近末尾处](#run-instance-complete-code)的示例显示了此片段的使用情况。

有关该[InstanceState](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceState.html)属性的有效值，`Instance.State.Code`请参阅类。

```
    //
    // Method to wait until the instances are running (or at least not pending)
    private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
    {
      Console.WriteLine(
        "\nWaiting for the instances to start." +
        "\nPress any key to stop waiting. (Response might be slightly delayed.)");

      int numberRunning;
      DescribeInstancesResponse responseDescribe;
      var requestDescribe = new DescribeInstancesRequest{
        InstanceIds = instanceIds};

      // Check every couple of seconds
      int wait = 2000;
      while(true)
      {
        // Get and check the status for each of the instances to see if it's past pending.
        // Once all instances are past pending, break out.
        // (For this example, we are assuming that there is only one reservation.)
        Console.Write(".");
        numberRunning = 0;
        responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
        foreach(Instance i in responseDescribe.Reservations[0].Instances)
        {
          // Check the lower byte of State.Code property
          // Code == 0 is the pending state
          if((i.State.Code & 255) > 0) numberRunning++;
        }
        if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
          break;

        // Wait a bit and try again (unless the user wants to stop waiting)
        Thread.Sleep(wait);
        if(Console.KeyAvailable)
          break;
      }

      Console.WriteLine("\nNo more instances are pending.");
      foreach(Instance i in responseDescribe.Reservations[0].Instances)
      {
        Console.WriteLine($"For {i.InstanceId}:");
        Console.WriteLine($"  VPC ID: {i.VpcId}");
        Console.WriteLine($"  Instance state: {i.State.Name}");
        Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
        Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
        Console.WriteLine($"  Key pair name: {i.KeyName}");
      }
    }
```

## 完整代码
<a name="run-instance-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c17c19b9c27b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)

  班级 [InstanceType](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceType.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [DescribeInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeInstancesRequest.html)

  班级 [DescribeInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeInstancesResponse.html)

  类 [Instance](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstance.html)

  班级 [InstanceNetworkInterfaceSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceNetworkInterfaceSpecification.html)

  班级 [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesRequest.html)

  班级 [RunInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesResponse.html)

### 代码
<a name="w2aac19c15c17c19b9c27b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2LaunchInstance
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to launch an EC2 instance
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string groupID =
        CommandLine.GetArgument(parsedArgs, null, "-g", "--group-id");
      string ami =
        CommandLine.GetArgument(parsedArgs, null, "-a", "--ami-id");
      string keyPairName =
        CommandLine.GetArgument(parsedArgs, null, "-k", "--keypair-name");
      string subnetID =
        CommandLine.GetArgument(parsedArgs, null, "-s", "--subnet-id");
      if(   (string.IsNullOrEmpty(groupID) || !groupID.StartsWith("sg-"))
         || (string.IsNullOrEmpty(ami) || !ami.StartsWith("ami-"))
         || (string.IsNullOrEmpty(keyPairName))
         || (!string.IsNullOrEmpty(subnetID) && !subnetID.StartsWith("subnet-")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create an EC2 client
      var ec2Client = new AmazonEC2Client();

      // Create an object with the necessary properties
      RunInstancesRequest request = GetRequestData(groupID, ami, keyPairName, subnetID);

      // Launch the instances and wait for them to start running
      var instanceIds = await LaunchInstances(ec2Client, request);
      await CheckState(ec2Client, instanceIds);
    }


    //
    // Method to put together the properties needed to launch the instance.
    private static RunInstancesRequest GetRequestData(
      string groupID, string ami, string keyPairName, string subnetID)
    {
      // Common properties
      var groupIDs = new List<string>() { groupID };
      var request = new RunInstancesRequest()
      {
        // The first three of these would be additional command-line arguments or similar.
        InstanceType = InstanceType.T1Micro,
        MinCount = 1,
        MaxCount = 1,
        ImageId = ami,
        KeyName = keyPairName
      };

      // Properties specifically for EC2 in a VPC.
      if(!string.IsNullOrEmpty(subnetID))
      {
        request.NetworkInterfaces =
          new List<InstanceNetworkInterfaceSpecification>() {
            new InstanceNetworkInterfaceSpecification() {
              DeviceIndex = 0,
              SubnetId = subnetID,
              Groups = groupIDs,
              AssociatePublicIpAddress = true
            }
          };
      }

      // Properties specifically for EC2-Classic
      else
      {
        request.SecurityGroupIds = groupIDs;
      }
      return request;
    }


    //
    // Method to launch the instances
    // Returns a list with the launched instance IDs
    private static async Task<List<string>> LaunchInstances(
      IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
    {
      var instanceIds = new List<string>();
      RunInstancesResponse responseLaunch =
        await ec2Client.RunInstancesAsync(requestLaunch);

      Console.WriteLine("\nNew instances have been created.");
      foreach (Instance item in responseLaunch.Reservation.Instances)
      {
        instanceIds.Add(item.InstanceId);
        Console.WriteLine($"  New instance: {item.InstanceId}");
      }

      return instanceIds;
    }


    //
    // Method to wait until the instances are running (or at least not pending)
    private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
    {
      Console.WriteLine(
        "\nWaiting for the instances to start." +
        "\nPress any key to stop waiting. (Response might be slightly delayed.)");

      int numberRunning;
      DescribeInstancesResponse responseDescribe;
      var requestDescribe = new DescribeInstancesRequest{
        InstanceIds = instanceIds};

      // Check every couple of seconds
      int wait = 2000;
      while(true)
      {
        // Get and check the status for each of the instances to see if it's past pending.
        // Once all instances are past pending, break out.
        // (For this example, we are assuming that there is only one reservation.)
        Console.Write(".");
        numberRunning = 0;
        responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
        foreach(Instance i in responseDescribe.Reservations[0].Instances)
        {
          // Check the lower byte of State.Code property
          // Code == 0 is the pending state
          if((i.State.Code & 255) > 0) numberRunning++;
        }
        if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
          break;

        // Wait a bit and try again (unless the user wants to stop waiting)
        Thread.Sleep(wait);
        if(Console.KeyAvailable)
          break;
      }

      Console.WriteLine("\nNo more instances are pending.");
      foreach(Instance i in responseDescribe.Reservations[0].Instances)
      {
        Console.WriteLine($"For {i.InstanceId}:");
        Console.WriteLine($"  VPC ID: {i.VpcId}");
        Console.WriteLine($"  Instance state: {i.State.Name}");
        Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
        Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
        Console.WriteLine($"  Key pair name: {i.KeyName}");
      }
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2LaunchInstance -g <group-id> -a <ami-id> -k <keypair-name> [-s <subnet-id>]" +
        "\n  -g, --group-id: The ID of the security group." +
        "\n  -a, --ami-id: The ID of an Amazon Machine Image." +
        "\n  -k, --keypair-name - The name of a key pair." +
        "\n  -s, --subnet-id: The ID of a subnet. Required only for EC2 in a VPC.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="run-instance-additional"></a>
+ 检查 EC2 实例的状态时，您可以向[DescribeInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeInstancesRequest.html)对象的`Filter`属性添加筛选器。使用这种技术，您可以将请求限制为某些实例；例如，带有特定用户指定标签的实例。
+ 为简洁起见，一些属性被赋予了典型值。这些属性中的任何或全部都可以通过编程方式或通过用户输入来确定。
+ 可用于[RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesRequest.html)对象`MinCount`和`MaxCount`属性的值由目标可用区和该实例类型允许的最大实例数决定。有关更多信息，请参阅 Amazon EC2 一般常见问题解答中的[我可以在 Amazon EC2 中运行多少个实例？](https://aws.amazon.com/ec2/faqs/#How_many_instances_can_I_run_in_Amazon_EC2)。
+ 如果您想使用与本示例不同的实例类型，有几个实例类型可供选择。有关更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的 [Amazon EC2 实例类型](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)。另请参阅[实例类型详细信息](https://aws.amazon.com/ec2/instance-types/)和[实例类型浏览器](https://aws.amazon.com/ec2/instance-explorer/)。
+ 您还可以在启动实例时将 [IAM 角色](net-dg-hosm.md)附加到实例。为此，请创建一个其`Name`属性设置为 IAM 角色名称的[IamInstanceProfileSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIamInstanceProfileSpecification.html)对象。然后将该对象添加到该[RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesRequest.html)对象的`IamInstanceProfile`属性中。
**注意**  
要启动附加了 IAM 角色的 EC2 实例，IAM 用户的配置必须包含某些权限。有关所需权限的更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[授予用户将 IAM 角色传递给实例的权限](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#permission-to-pass-iam-roles)。

## （可选）连接到实例
<a name="connect-to-instance"></a>

在实例运行之后，您可以使用合适的远程客户端远程连接该实例。对于 Linux 和 Windows 实例，您需要实例的公有 IP 地址或公有 DNS 名称。您还需要以下项目。

**对于 Linux 实例**

您可以使用 SSH 客户端接到您的 Linux 实例。确保启动实例时使用的安全组允许端口 22 上的 SSH 流量，如[更新安全组](authorize-ingress.md)中所述。

您还需要用于启动实例的密钥对私有部分；即 PEM 文件。

有关更多信息，请参阅《Amazon EC2 用户指南》中的[连接到 Linux 实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html)。

**对于 Windows 实例**

您可以使用 RDP 客户端连接到您的实例。确保启动实例时使用的安全组允许端口 3389 上的 RDP 流量，如[更新安全组](authorize-ingress.md)中所述。

您还需要管理员密码。您可以使用以下示例代码来获取此信息，该代码需要实例 ID 和用于启动实例的密钥对的私有部分，即 PEM 文件。

有关更多信息，请参阅《Amazon EC2 用户指南》中的[连接到 Windows 实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connecting_to_windows_instance.html)。

**警告**  
此示例代码返回您实例的纯文本管理员密码。

### SDK 参考
<a name="w2aac19c15c17c19b9c35c23b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [GetPasswordDataRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TGetPasswordDataRequest.html)

  班级 [GetPasswordDataResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TGetPasswordDataResponse.html)

### 代码
<a name="w2aac19c15c17c19b9c35c25b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2GetWindowsPassword
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to get the Administrator password of a Windows EC2 instance
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string instanceID =
        CommandLine.GetArgument(parsedArgs, null, "-i", "--instance-id");
      string pemFileName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--pem-filename");
      if(   (string.IsNullOrEmpty(instanceID) || !instanceID.StartsWith("i-"))
         || (string.IsNullOrEmpty(pemFileName) || !pemFileName.EndsWith(".pem")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Get and display the password
      string password = await GetPassword(ec2Client, instanceID, pemFileName);
      Console.WriteLine($"\nPassword: {password}");
    }


    //
    // Method to get the administrator password of a Windows EC2 instance
    private static async Task<string> GetPassword(
      IAmazonEC2 ec2Client, string instanceID, string pemFilename)
    {
      string password = string.Empty;
      GetPasswordDataResponse response =
        await ec2Client.GetPasswordDataAsync(new GetPasswordDataRequest{
          InstanceId = instanceID});
      if(response.PasswordData != null)
      {
        password = response.GetDecryptedPassword(File.ReadAllText(pemFilename));
      }
      else
      {
        Console.WriteLine($"\nThe password is not available for instance {instanceID}.");
        Console.WriteLine($"If this is a Windows instance, the password might not be ready.");
      }
      return password;
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2GetWindowsPassword -i <instance-id> -p pem-filename" +
        "\n  -i, --instance-id: The name of the EC2 instance." +
        "\n  -p, --pem-filename: The name of the PEM file with the private key.");
    }
  }

  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 清理
<a name="run-instance-cleanup"></a>

您不再需要 EC2 实例时，请确保终止该实例，如[终止 Amazon EC2 实例](terminate-instance.md)中所述。

# 终止 Amazon EC2 实例
<a name="terminate-instance"></a>

如果您不再需要一个或多个 Amazon EC2 实例，则可终止这些实例。

此示例向您展示如何使用终止 EC2 实例。 适用于 .NET 的 AWS SDK 它以实例 ID 作为输入。

## SDK 参考
<a name="w2aac19c15c17c19c11b7b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesRequest.html)

  班级 [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesResponse.html)

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2TerminateInstance
{
  class Program
  {
    static async Task Main(string[] args)
    {
      if((args.Length == 1) && (args[0].StartsWith("i-")))
      {
        // Terminate the instance
        var ec2Client = new AmazonEC2Client();
        await TerminateInstance(ec2Client, args[0]);
      }
      else
      {
        Console.WriteLine("\nCommand-line argument missing or incorrect.");
        Console.WriteLine("\nUsage: EC2TerminateInstance instance-ID");
        Console.WriteLine("  instance-ID - The EC2 instance you want to terminate.");
        return;
      }
    }

    //
    // Method to terminate an EC2 instance
    private static async Task TerminateInstance(IAmazonEC2 ec2Client, string instanceID)
    {
      var request = new TerminateInstancesRequest{
        InstanceIds = new List<string>() { instanceID }};
      TerminateInstancesResponse response =
        await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
          InstanceIds = new List<string>() { instanceID }
        });
      foreach (InstanceStateChange item in response.TerminatingInstances)
      {
        Console.WriteLine("Terminated instance: " + item.InstanceId);
        Console.WriteLine("Instance state: " + item.CurrentState.Name);
      }
    }
  }
}
```

运行该示例后，最好登录 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/)来验证 [EC2 实例](https://console.aws.amazon.com/ec2/v2/home#Instances)是否已终止。

# Amazon EC2 竞价型实例教程
<a name="how-to-spot-instances"></a>

本教程向您展示如何使用管理 Amazon EC2 竞价型实例。 适用于 .NET 的 AWS SDK 

## 概述
<a name="tutor-spot-net-overview"></a>

竞价型实例允许您以低于按需价格请求未使用的 Amazon EC2 容量。这可以显著降低可能被中断的应用程序的 EC2 成本。

下面简要概述了如何请求和使用竞价型实例。

1. 创建竞价型实例请求，指定您愿意支付的最高价格。

1. 请求完成后，请像运行任何其它 Amazon EC2 实例一样运行该实例。

1. 根据需要运行该实例，然后将其终止，除非 *Spot 价格*发生变化导致实例终止。

1. 在不再需要竞价型实例请求时对其进行清理，这样就不会再创建竞价型实例。

这是对竞价型实例整体层面的概述。要更好地了解竞价型实例，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[竞价型实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html)。

## 关于本教程
<a name="about-spot-instances-tutorial"></a>

在学习本教程时，您可以使用 适用于 .NET 的 AWS SDK 来执行以下操作：
+ 创建竞价型实例请求
+ 确定何时执行该竞价型实例请求
+ 取消竞价型实例请求
+ 终止相关实例

以下各节提供了此示例的片段和其它信息。片段后显示了[该示例的完整代码](#tutor-spot-net-main)，并且可以按原样构建和运行。

**Topics**
+ [概述](#tutor-spot-net-overview)
+ [关于本教程](#about-spot-instances-tutorial)
+ [先决条件](#tutor-spot-net-prereq)
+ [收集所需内容](#tutor-spot-net-gather)
+ [创建竞价型实例请求](#tutor-spot-net-submit)
+ [确定您的竞价型实例请求的状态](#tutor-spot-net-request-state)
+ [清理您的竞价型实例请求](#tutor-spot-net-clean-up-request)
+ [清理您的竞价型实例](#tutor-spot-net-clean-up-instance)
+ [完整代码](#tutor-spot-net-main)
+ [其他注意事项](#tutor-spot-net-additional)

## 先决条件
<a name="tutor-spot-net-prereq"></a>

有关 APIs 和先决条件的信息，请参阅父部分 ([使用 Amazon EC2](ec2-apis-intro.md))。

## 收集所需内容
<a name="tutor-spot-net-gather"></a>

要创建竞价型实例请求，您需要一些东西。
+ 实例数量和其实例类型。有几个实例类型可供选择。有关更多信息，请参阅《Amazon EC2 用户指南》[https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的 [Amazon EC2 实例类型](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)。另请参阅[实例类型详细信息](https://aws.amazon.com/ec2/instance-types/)和[实例类型浏览器](https://aws.amazon.com/ec2/instance-explorer/)。

  在本教程中，默认数量为 1。
+ 要用于创建实例的自定义亚马逊机器映像（AMI）的 ID。有关信息 AMIs，请参阅 [Amazon [EC2 用户指南中的亚马逊系统](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)映像 (AMIs)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)。具体而言，请参阅[查找 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html) 并[共享 AMIs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)。
+ 您每小时愿意为每个实例支付的最高价格。您可以在 [Amazon EC2 定价页面](https://aws.amazon.com/ec2/pricing/)上查看所有实例类型（包括按需实例和竞价型实例）的价格。本教程的默认价格将在后面说明。
+ 如果您想远程连接到实例，则需要具有适当配置和资源的安全组。[使用 Amazon EC2 中的安全组](security-groups.md)中对此进行了描述，并在[启动 Amazon EC2 实例](run-instance.md)中提供了有关[收集所需内容](run-instance.md#run-instance-gather)和[连接到实例](run-instance.md#connect-to-instance)的信息。为简单起见，本教程使用了所有新 AWS 账户都拥有的名为 **default** 的安全组。

有很多方法可以请求 Spot 实例。以下是常见策略：
+ 提出请求以确保成本低于按需定价。
+ 基于最终的计算值提出请求。
+ 提出请求以便尽快获得计算容量。

以下解释参考了 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[竞价型实例定价历史记录](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances-history.html)。

### 降低成本至低于按需价格
<a name="reduce-cost"></a>

您需要进行花费数小时或数天的批处理工作。然而，您可以灵活调整启动和结束时间。您希望看到是否以低于按需实例的成本完成。

您可以通过使用 Amazon EC2 控制台或 Amazon EC2 API 来检查各个类型实例的 Spot 价格历史记录。在您分析了给定可用区内所需实例类型的价格记录之后，您有两种可供选择的方法发出请求：
+ 指定在 Spot 价格范围（这仍然低于按需定价）的上限发出请求，预测您单次竞价型实例请求很有可能会达成，并运行足够的连续计算时间来完成此项工作。
+ 指定在 Spot 价格范围的下限发出请求，随着时间的推移，通过持久的请求，计划结合多种已启动实例。总计一下，该实例会以较低的总成本、花费很长时间来完成这项工作。

### 支付不超过该结果的值
<a name="value-of-result"></a>

您需要进行数据处理工作。您将会对该工作的结果有一个很好的了解，以便于能够让您知道在计算成本方面它们的价值。

当您分析了实例类型的 Spot 价格历史记录之后，选择一个计算时间成本不高于该工作结果成本的价格。由于 Spot 价格的波动，该价格可能会达到或低于您的请求，所以您要创建一个持久请求，并允许它间歇运行。

### 快速获取计算容量
<a name="acquire-quickly"></a>

您对附加容量有一个无法预料的短期需求，该容量不能通过按需实例获取。当您分析了实例类型的 Spot 价格历史记录之后，您选择高于历史最高价格的价格，以大幅提高完成您请求的可能性，并继续计算，直到完成实例。

收集所需内容并选择策略后，就可以请求竞价型实例了。对于本教程，默认的最高 Spot 实例价格设置为与按需价格相同（本教程为 0.003 美元）。以这种方式设置价格可最大限度地提高请求被执行的机会。

## 创建竞价型实例请求
<a name="tutor-spot-net-submit"></a>

以下代码片段向您展示了如何使用之前收集的元素创建竞价型实例请求。

[本主题末尾](#tutor-spot-net-main)的示例显示了此片段的使用情况。

```
    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }
```

此方法返回的重要值是竞价型实例请求 ID，它包含在返回[SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSpotInstanceRequest.html)对象的`SpotInstanceRequestId`成员中。

**注意**  
您需要为启动的任何竞价型实例付费。为避免不必要的开支，请务必[取消所有请求](#tutor-spot-net-clean-up-request)并[终止所有实例](#tutor-spot-net-clean-up-instance)。

## 确定您的竞价型实例请求的状态
<a name="tutor-spot-net-request-state"></a>

以下代码片段向您展示了如何获取有关您竞价型实例请求的信息。您可以使用这些信息在代码中做出某些决定，例如是否继续等待竞价型实例请求得到满足。

[本主题末尾](#tutor-spot-net-main)的示例显示了此片段的使用情况。

```
    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }
```

该方法返回有关竞价型实例请求的信息，例如实例 ID、其状态和状态代码。有关竞价型实例请求状态代码的更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[竞价型实例请求状态](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html#spot-instance-bid-status-understand)。

## 清理您的竞价型实例请求
<a name="tutor-spot-net-clean-up-request"></a>

当您不再需要请求竞价型实例时，请务必取消任何未处理的请求，以防止这些请求被重新执行。以下代码片段演示了如何取消一个竞价型实例请求。

[本主题末尾](#tutor-spot-net-main)的示例显示了此片段的使用情况。

```
    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }
```

## 清理您的竞价型实例
<a name="tutor-spot-net-clean-up-instance"></a>

为避免不必要的费用，终止任何从竞价型实例请求启动的实例非常重要；只是取消竞价型实例请求并不会终止您的实例，这意味着您需要继续为它们支付费用。以下代码片段演示了在获取活动竞价型实例的实例标识符后如何终止实例。

[本主题末尾](#tutor-spot-net-main)的示例显示了此片段的使用情况。

```
    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
```

## 完整代码
<a name="tutor-spot-net-main"></a>

以下代码示例调用前面描述的方法来创建和取消竞价型实例请求并终止竞价型实例。

### SDK 参考
<a name="w2aac19c15c17c21c43b5b1"></a>

NuGet 包裹：
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

编程元素：
+ 命名空间 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  [Amazon EC2 客户端](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)

  班级 [InstanceType](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceType.html)
+ 命名空间 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  班级 [CancelSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCancelSpotInstanceRequestsRequest.html)

  班级 [DescribeSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSpotInstanceRequestsRequest.html)

  班级 [DescribeSpotInstanceRequestsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSpotInstanceRequestsResponse.html)

  班级 [InstanceStateChange](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceStateChange.html)

  班级 [LaunchSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TLaunchSpecification.html)

  班级 [RequestSpotInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRequestSpotInstancesRequest.html)

  班级 [RequestSpotInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRequestSpotInstancesResponse.html)

  班级 [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSpotInstanceRequest.html)

  班级 [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesRequest.html)

  班级 [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesResponse.html)

### 代码
<a name="w2aac19c15c17c21c43b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2SpotInstanceRequests
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Some default values.
      // These could be made into command-line arguments instead.
      var instanceType = InstanceType.T1Micro;
      string securityGroupName = "default";
      string spotPrice = "0.003";
      int instanceCount = 1;

      // Parse the command line arguments
      if((args.Length != 1) || (!args[0].StartsWith("ami-")))
      {
        Console.WriteLine("\nUsage: EC2SpotInstanceRequests ami");
        Console.WriteLine("  ami: the Amazon Machine Image to use for the Spot Instances.");
        return;
      }

      // Create the Amazon EC2 client.
      var ec2Client = new AmazonEC2Client();

      // Create the Spot Instance request and record its ID
      Console.WriteLine("\nCreating spot instance request...");
      var req = await CreateSpotInstanceRequest(
        ec2Client, args[0], securityGroupName, instanceType, spotPrice, instanceCount);
      string requestId = req.SpotInstanceRequestId;

      // Wait for an EC2 Spot Instance to become active
      Console.WriteLine(
        $"Waiting for Spot Instance request with ID {requestId} to become active...");
      int wait = 1;
      var start = DateTime.Now;
      while(true)
      {
        Console.Write(".");

        // Get and check the status to see if the request has been fulfilled.
        var requestInfo = await GetSpotInstanceRequestInfo(ec2Client, requestId);
        if(requestInfo.Status.Code == "fulfilled")
        {
          Console.WriteLine($"\nSpot Instance request {requestId} " +
            $"has been fulfilled by instance {requestInfo.InstanceId}.\n");
          break;
        }

        // Wait a bit and try again, longer each time (1, 2, 4, ...)
        Thread.Sleep(wait);
        wait = wait * 2;
      }

      // Show the user how long it took to fulfill the Spot Instance request.
      TimeSpan span = DateTime.Now.Subtract(start);
      Console.WriteLine($"That took {span.TotalMilliseconds} milliseconds");

      // Perform actions here as needed.
      // For this example, simply wait for the user to hit a key.
      // That gives them a chance to look at the EC2 console to see
      // the running instance if they want to.
      Console.WriteLine("Press any key to start the cleanup...");
      Console.ReadKey(true);

      // Cancel the request.
      // Do this first to make sure that the request can't be re-fulfilled
      // once the Spot Instance has been terminated.
      Console.WriteLine("Canceling Spot Instance request...");
      await CancelSpotInstanceRequest(ec2Client, requestId);

      // Terminate the Spot Instance that's running.
      Console.WriteLine("Terminating the running Spot Instance...");
      await TerminateSpotInstance(ec2Client, requestId);

      Console.WriteLine("Done. Press any key to exit...");
      Console.ReadKey(true);
    }


    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }


    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }


    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }


    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
  }
}
```

## 其他注意事项
<a name="tutor-spot-net-additional"></a>
+ 运行本教程后，最好登录 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/)，验证[竞价型实例请求](https://console.aws.amazon.com/ec2/home#SpotInstances:)是否已取消，[竞价型实例](https://console.aws.amazon.com/ec2/v2/home#Instances)是否已终止。

# 使用访问 AWS Identity and Access Management (IAM) 适用于 .NET 的 AWS SDK
<a name="iam-apis-intro"></a>

 适用于 .NET 的 AWS SDK 支持 [AWS Identity and Access Management](https://docs.aws.amazon.com/IAM/latest/UserGuide/)，这是一项 Web 服务，可让 AWS 客户在中管理用户和用户权限 AWS。

 AWS Identity and Access Management (IAM) *用户*是您在中创建的实体 AWS。实体代表与 AWS之交互的个人或应用程序。有关 IAM 用户的更多信息，请参阅《IAM 用户指南》中的 [IAM 用户](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html)以及 [IAM 和 STS 限制](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html)。

您通过创建 IAM *策略*向用户授予权限。策略包含一个*策略文档*，其中列出了用户可以执行的操作以及这些操作会影响的资源。有关 IAM 策略的更多信息，请参阅《IAM 用户指南》**中的[策略和权限](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html)。

**警告**  
为了避免安全风险，在开发专用软件或处理真实数据时，请勿使用 IAM 用户进行身份验证，而是使用与身份提供者的联合身份验证，例如 [AWS IAM Identity Center](https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html)。

## APIs
<a name="w2aac19c15c19c13"></a>

 适用于 .NET 的 AWS SDK 提供给 I APIs AM 客户端。 APIs 使您可以使用 IAM 功能，例如用户、角色和访问密钥。

本节包含少量示例，向您展示使用这些示例时可以遵循的模式 APIs。要查看全套内容 APIs，请参阅 [适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)（并滚动至 “Amazon. IdentityManagement“)。

本节还包含[一个示例](net-dg-hosm.md)，向您展示如何将 IAM 角色附加到 Amazon EC2 实例，以便更轻松地管理凭证。

IAM APIs 由提供[AWSSDK。 IdentityManagement](https://www.nuget.org/packages/AWSSDK.IdentityManagement) NuGet包裹。

## 先决条件
<a name="w2aac19c15c19c15"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

## 主题
<a name="w2aac19c15c19c17"></a>

**Topics**
+ [APIs](#w2aac19c15c19c13)
+ [先决条件](#w2aac19c15c19c15)
+ [主题](#w2aac19c15c19c17)
+ [通过 JSON 创建托管式策略](iam-policies-create-json.md)
+ [显示策略文档](iam-policies-display.md)
+ [使用角色授予访问权限](net-dg-hosm.md)

# 通过 JSON 创建 IAM 托管式策略
<a name="iam-policies-create-json"></a>

此示例向您展示如何使用根据给定的 JSON [策略文档创建 IAM 托管](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies)策略。 适用于 .NET 的 AWS SDK 该应用程序创建 IAM 客户端对象，从文件中读取策略文档，然后创建策略。

**注意**  
有关 JSON 格式的示例策略文档，请参阅本主题末尾的[其它注意事项](#iam-policies-create-json-additional)。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#iam-policies-create-json-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [创建策略](#iam-policies-create-json-create)
+ [完整代码](#iam-policies-create-json-complete-code)
+ [其他注意事项](#iam-policies-create-json-additional)

## 创建策略
<a name="iam-policies-create-json-create"></a>

以下代码片段使用给定名称和策略文档创建一个 IAM 托管式策略。

[本主题末尾](#iam-policies-create-json-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to create an IAM policy from a JSON file
    private static async Task<CreatePolicyResponse> CreateManagedPolicy(
      IAmazonIdentityManagementService iamClient, string policyName, string jsonFilename)
    {
      return await iamClient.CreatePolicyAsync(new CreatePolicyRequest{
        PolicyName = policyName,
        PolicyDocument = File.ReadAllText(jsonFilename)});
    }
```

## 完整代码
<a name="iam-policies-create-json-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c19c21c17b5b1"></a>

NuGet 包裹：
+ [AWSSDK.IdentityManagement](https://www.nuget.org/packages/AWSSDK.IdentityManagement)

编程元素：
+ 命名空间 [Amazon。 IdentityManagement](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/NIAM.html)

  班级 [AmazonIdentityManagementServiceClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TIAMServiceClient.html)
+ 命名空间 [Amazon。 IdentityManagement.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/NIAMModel.html)

  班级 [CreatePolicyRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TCreatePolicyRequest.html)

  班级 [CreatePolicyResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TCreatePolicyResponse.html)

### 代码
<a name="w2aac19c15c19c21c17b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.IdentityManagement;
using Amazon.IdentityManagement.Model;

namespace IamCreatePolicyFromJson
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create an IAM policy with a given policy document
  class Program
  {
    private const int MaxArgs = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if((parsedArgs.Count == 0) || (parsedArgs.Count > MaxArgs))
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string policyName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--policy-name");
      string policyFilename =
        CommandLine.GetArgument(parsedArgs, null, "-j", "--json-filename");
      if(   string.IsNullOrEmpty(policyName)
         || (string.IsNullOrEmpty(policyFilename) || !policyFilename.EndsWith(".json")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create an IAM service client
      var iamClient = new AmazonIdentityManagementServiceClient();

      // Create the new policy
      var response = await CreateManagedPolicy(iamClient, policyName, policyFilename);
      Console.WriteLine($"\nPolicy {response.Policy.PolicyName} has been created.");
      Console.WriteLine($"  Arn: {response.Policy.Arn}");
    }


    //
    // Method to create an IAM policy from a JSON file
    private static async Task<CreatePolicyResponse> CreateManagedPolicy(
      IAmazonIdentityManagementService iamClient, string policyName, string jsonFilename)
    {
      return await iamClient.CreatePolicyAsync(new CreatePolicyRequest{
        PolicyName = policyName,
        PolicyDocument = File.ReadAllText(jsonFilename)});
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: IamCreatePolicyFromJson -p <policy-name> -j <json-filename>" +
        "\n  -p, --policy-name: The name you want the new policy to have." +
        "\n  -j, --json-filename: The name of the JSON file with the policy document.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="iam-policies-create-json-additional"></a>
+ 以下是示例策略文档，您可以将其复制到 JSON 文件中并用作此应用程序的输入：

------
#### [ JSON ]

****  

  ```
  {
    "Version":"2012-10-17",		 	 	 
    "Id"  : "DotnetTutorialPolicy",
    "Statement" : [
      {
        "Sid" : "DotnetTutorialPolicyS3",
        "Effect" : "Allow",
        "Action" : [
          "s3:Get*",
          "s3:List*"
        ],
        "Resource" : "*"
      },
      {
        "Sid" : "DotnetTutorialPolicyPolly",
        "Effect": "Allow",
        "Action": [
          "polly:DescribeVoices",
          "polly:SynthesizeSpeech"
        ],
        "Resource": "*"
      }
    ]
  }
  ```

------
+ 您可以通过查看 [IAM 控制台](https://console.aws.amazon.com/iam/home#/policies)来验证策略是否已创建。使用**筛选策略**下拉菜单，选择**客户托管**。在您不再需要策略时将其删除。
+  有关创建策略的更多信息，请参阅 [IAM 用户指南](https://docs.aws.amazon.com/IAM/latest/UserGuide/)中的[创建 IAM 策略](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html)和 [IAM JSON 策略参考](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html)

# 显示 IAM 托管式策略的策略文档
<a name="iam-policies-display"></a>

此示例向您展示如何使用 适用于 .NET 的 AWS SDK 来显示策略文档。该应用程序创建 IAM 客户端对象，找到给定 IAM 托管式策略的默认版本，然后以 JSON 格式显示策略文档。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#iam-policies-display-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [查找默认版本](#iam-policies-display-version)
+ [显示策略文档](#iam-policies-display-doc)
+ [完整代码](#iam-policies-display-complete-code)

## 查找默认版本
<a name="iam-policies-display-version"></a>

以下代码片段查找给定 IAM 策略的默认版本。

[本主题末尾](#iam-policies-display-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to determine the default version of an IAM policy
    // Returns a string with the version
    private static async Task<string> GetDefaultVersion(
      IAmazonIdentityManagementService iamClient, string policyArn)
    {
      // Retrieve all the versions of this policy
      string defaultVersion = string.Empty;
      ListPolicyVersionsResponse reponseVersions =
        await iamClient.ListPolicyVersionsAsync(new ListPolicyVersionsRequest{
          PolicyArn = policyArn});

      // Find the default version
      foreach(PolicyVersion version in reponseVersions.Versions)
      {
        if(version.IsDefaultVersion)
        {
          defaultVersion = version.VersionId;
          break;
        }
      }

      return defaultVersion;
    }
```

## 显示策略文档
<a name="iam-policies-display-doc"></a>

以下代码片段以 JSON 格式显示了给定 IAM 策略的策略文档。

[本主题末尾](#iam-policies-display-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to retrieve and display the policy document of an IAM policy
    private static async Task ShowPolicyDocument(
      IAmazonIdentityManagementService iamClient, string policyArn, string defaultVersion)
    {
      // Retrieve the policy document of the default version
      GetPolicyVersionResponse responsePolicy =
        await iamClient.GetPolicyVersionAsync(new GetPolicyVersionRequest{
          PolicyArn = policyArn,
          VersionId = defaultVersion});

      // Display the policy document (in JSON)
      Console.WriteLine($"Version {defaultVersion} of the policy (in JSON format):");
      Console.WriteLine(
        $"{HttpUtility.UrlDecode(responsePolicy.PolicyVersion.Document)}");
    }
```

## 完整代码
<a name="iam-policies-display-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c19c23c19b5b1"></a>

NuGet 包裹：
+ [AWSSDK.IdentityManagement](https://www.nuget.org/packages/AWSSDK.IdentityManagement)

编程元素：
+ 命名空间 [Amazon。 IdentityManagement](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/NIAM.html)

  班级 [AmazonIdentityManagementServiceClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TIAMServiceClient.html)
+ 命名空间 [Amazon。 IdentityManagement.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/NIAMModel.html)

  班级 [GetPolicyVersionRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TGetPolicyVersionRequest.html)

  班级 [GetPolicyVersionResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TGetPolicyVersionResponse.html)

  班级 [ListPolicyVersionsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TListPolicyVersionsRequest.html)

  班级 [ListPolicyVersionsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TListPolicyVersionsResponse.html)

  班级 [PolicyVersion](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/IAM/TPolicyVersion.html)

### 代码
<a name="w2aac19c15c19c23c19b7b1"></a>

```
using System;
using System.Web;
using System.Threading.Tasks;
using Amazon.IdentityManagement;
using Amazon.IdentityManagement.Model;

namespace IamDisplayPolicyJson
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      if(args.Length != 1)
      {
        Console.WriteLine("\nUsage: IamDisplayPolicyJson policy-arn");
        Console.WriteLine("   policy-arn: The ARN of the policy to retrieve.");
        return;
      }
      if(!args[0].StartsWith("arn:"))
      {
        Console.WriteLine("\nCould not find policy ARN in the command-line arguments:");
        Console.WriteLine($"{args[0]}");
        return;
      }

      // Create an IAM service client
      var iamClient = new AmazonIdentityManagementServiceClient();

      // Retrieve and display the policy document of the given policy
      string defaultVersion = await GetDefaultVersion(iamClient, args[0]);
      if(string.IsNullOrEmpty(defaultVersion))
        Console.WriteLine($"Could not find the default version for policy {args[0]}.");
      else
        await ShowPolicyDocument(iamClient, args[0], defaultVersion);
    }


    //
    // Method to determine the default version of an IAM policy
    // Returns a string with the version
    private static async Task<string> GetDefaultVersion(
      IAmazonIdentityManagementService iamClient, string policyArn)
    {
      // Retrieve all the versions of this policy
      string defaultVersion = string.Empty;
      ListPolicyVersionsResponse reponseVersions =
        await iamClient.ListPolicyVersionsAsync(new ListPolicyVersionsRequest{
          PolicyArn = policyArn});

      // Find the default version
      foreach(PolicyVersion version in reponseVersions.Versions)
      {
        if(version.IsDefaultVersion)
        {
          defaultVersion = version.VersionId;
          break;
        }
      }

      return defaultVersion;
    }


    //
    // Method to retrieve and display the policy document of an IAM policy
    private static async Task ShowPolicyDocument(
      IAmazonIdentityManagementService iamClient, string policyArn, string defaultVersion)
    {
      // Retrieve the policy document of the default version
      GetPolicyVersionResponse responsePolicy =
        await iamClient.GetPolicyVersionAsync(new GetPolicyVersionRequest{
          PolicyArn = policyArn,
          VersionId = defaultVersion});

      // Display the policy document (in JSON)
      Console.WriteLine($"Version {defaultVersion} of the policy (in JSON format):");
      Console.WriteLine(
        $"{HttpUtility.UrlDecode(responsePolicy.PolicyVersion.Document)}");
    }
  }
}
```

# 使用 IAM 角色授予访问权限
<a name="net-dg-hosm"></a>

本教程向您展示如何使用在 Amazon EC2 实例上启用 IAM 角色。 适用于 .NET 的 AWS SDK 

## 概述
<a name="hosm-overview"></a>

对的所有请求都 AWS 必须使用颁发的凭证进行加密签名。 AWS因此，您需要通过某种策略为 Amazon EC2 实例上运行的应用程序管理凭证。您必须以安全的方式分发、存储和轮换这些凭证，并且能让应用程序访问这些凭证。

使用 IAM 角色，您可以有效地管理这些凭证。您可以创建 IAM 角色并使用应用程序所需的权限对其进行配置，然后将该角色附加到 EC2 实例。要详细了解使用 IAM 角色的优势，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的 [Amazon EC2 的 IAM 角色](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)。另外，有关 [IAM 角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)的信息，请参阅 IAM 用户指南。

对于使用构建的应用程序 适用于 .NET 的 AWS SDK，当应用程序为 AWS 服务构造客户端对象时，该对象会搜索来自多个潜在来源的证书。它搜索的顺序显示在[凭证和配置文件解析](creds-assign.md)中。

如果客户端对象在任何其它来源中找不到凭证，它会检索与配置为 IAM 角色且位于 EC2 实例元数据中的凭证具有相同权限的临时凭证。这些凭据用于 AWS 从客户端对象对进行调用。

## 关于本教程
<a name="about-hosm-tutorial"></a>

在学习本教程时，您可以使用 适用于 .NET 的 AWS SDK （和其他工具）启动附加了 IAM 角色的 Amazon EC2 实例，然后使用 IAM 角色的权限查看该实例上的应用程序。

**Topics**
+ [概述](#hosm-overview)
+ [关于本教程](#about-hosm-tutorial)
+ [创建示例 Amazon S3 应用程序](#net-dg-hosm-sample-s3-app)
+ [创建一个 IAM 角色](#net-dg-hosm-create-the-role)
+ [启动 EC2 实例并指定 IAM 角色](#net-dg-hosm-launch-ec2-instance)
+ [连接到 EC2 实例](#net-dg-hosm-connect)
+ [在 EC2 实例上运行示例应用程序](#net-dg-hosm-run-the-app)
+ [清理](#net-dg-hosm-cleanup)

## 创建示例 Amazon S3 应用程序
<a name="net-dg-hosm-sample-s3-app"></a>

此示例应用程序从 Amazon S3 中检索对象。要运行示例应用程序，您需要：
+ 包含文本文件的 Amazon S3 桶。
+ AWS 开发计算机上允许您访问存储桶的凭据。

有关创建 Amazon S3 存储桶并上载对象的信息，请参阅 [Amazon Simple Storage Service 用户指南](https://docs.aws.amazon.com/AmazonS3/latest/userguide/)。有关 AWS 证书的信息，请参阅[使用配置 SDK 身份验证 AWS](creds-idc.md)。

使用以下代码创建 .NET Core 项目。然后在开发计算机上测试该应用程序。

**注意**  
在您的开发计算机上，安装了 .NET Core 运行时系统，这使您无需发布即可运行应用程序。在本教程中稍后创建 EC2 实例时，您可以选择在实例上安装 .NET Core 运行时系统。这为您提供了类似的体验和较小的文件传输。  
 但是，您也可以选择不在实例上安装 .NET Core 运行时系统。如果您选择此操作方案，则必须发布应用程序，以便在将其转移到实例时包含所有依赖关系。

### SDK 参考
<a name="w2aac19c15c19c25c17c13b1"></a>

NuGet 包裹：
+ [AWSSDK.S3](https://www.nuget.org/packages/AWSSDK.S3)

编程元素：
+ 命名空间 [Amazon.S3](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/NS3.html)

  类 [AmazonS3Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TS3Client.html)
+ 命名空间 [Amazon.S3.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/NS3Model.html)

  班级 [GetObjectResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TGetObjectResponse.html)

### 代码
<a name="w2aac19c15c19c25c17c15b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.S3;
using Amazon.S3.Model;

namespace S3GetTextItem
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to retrieve a text file from an S3 bucket and write it to a local file
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string bucket =
        CommandLine.GetArgument(parsedArgs, null, "-b", "--bucket-name");
      string item =
        CommandLine.GetArgument(parsedArgs, null, "-t", "--text-object");
      string outFile =
        CommandLine.GetArgument(parsedArgs, null, "-o", "--output-filename");
      if(   string.IsNullOrEmpty(bucket)
         || string.IsNullOrEmpty(item)
         || string.IsNullOrEmpty(outFile))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the S3 client object and get the file object from the bucket.
      var response = await GetObject(new AmazonS3Client(), bucket, item);

      // Write the contents of the file object to the given output file.
      var reader = new StreamReader(response.ResponseStream);
      string contents = reader.ReadToEnd();
      using (var s = new FileStream(outFile, FileMode.Create))
      using (var writer = new StreamWriter(s))
        writer.WriteLine(contents);
    }


    //
    // Method to get an object from an S3 bucket.
    private static async Task<GetObjectResponse> GetObject(
      IAmazonS3 s3Client, string bucket, string item)
    {
        Console.WriteLine($"Retrieving {item} from bucket {bucket}.");
        return await s3Client.GetObjectAsync(bucket, item);
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: S3GetTextItem -b <bucket-name> -t <text-object> -o <output-filename>" +
        "\n  -b, --bucket-name: The name of the S3 bucket." +
        "\n  -t, --text-object: The name of the text object in the bucket." +
        "\n  -o, --output-filename: The name of the file to write the text to.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

如果需要，可以临时删除在开发计算机上使用的凭证，以查看应用程序的响应情况。（但请务必在完成后恢复凭证。）

## 创建一个 IAM 角色
<a name="net-dg-hosm-create-the-role"></a>

创建具有合适权限以访问 Amazon S3 的 IAM 角色。

1. 打开 [IAM 控制台](https://console.aws.amazon.com/iam/)。

1. 在导航窗格中，选择**角色**，然后选择**创建角色**。

1. 选择 **AWS 服务**，找到并选择 **EC2**，然后选择**下一步: 权限**。

1. 在 “**附加权限策略**” 下，找到并选择 **AmazonS3 ReadOnlyAccess**。如果愿意，请查看该策略，然后选择**下一步: 标签**。

1. 如果需要，可以添加标签，然后选择**下一步: 查看**。

1. 键入角色的名称和描述，然后选择**创建角色**。请记住此名称，因为在启动 EC2 实例时会用到它。

## 启动 EC2 实例并指定 IAM 角色
<a name="net-dg-hosm-launch-ec2-instance"></a>

使用先前创建的 IAM 角色启动 EC2 实例。您可以通过下列方式来执行此操作。
+ **使用 EC2 控制台**

  要使用 EC2 控制台启动实例，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[使用新的启动实例向导启动实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-instance-wizard.html)。

  浏览启动页面时，您至少应展开**高级详细信息**窗格，以便指定之前在 **IAM 实例配置文件**中创建的 IAM 角色。
+ **使用 适用于 .NET 的 AWS SDK**

  有关此操作的信息，请参阅[启动 Amazon EC2 实例](run-instance.md)，包括该主题接近末尾处的[其他注意事项](run-instance.md#run-instance-additional)。

要启动附加了 IAM 角色的 EC2 实例，IAM 用户的配置必须包含某些权限。有关所需权限的更多信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[授予用户将 IAM 角色传递给实例的权限](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#permission-to-pass-iam-roles)。

## 连接到 EC2 实例
<a name="net-dg-hosm-connect"></a>

连接到 EC2 实例，以便您可将示例应用程序传输到该实例，然后运行该应用程序。您将需要包含用于启动实例的密钥对私有部分的文件；即 PEM 文件。

有关如何连接到实例的信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[连接到 Linux 实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html)或[连接到 Windows 实例](https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/connecting_to_windows_instance.html)。当您连接时，请确保您可以将文件从开发计算机传输到您的实例。

如果您在 Windows 上使用 Visual Studio，也可以使用 Toolkit for Visual Studio 连接到实例。有关更多信息，请参阅 AWS Toolkit for Visual Studio 用户指南中的[连接到 Amazon EC2 实例](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/tkv-ec2-ami.html#connect-ec2)。

## 在 EC2 实例上运行示例应用程序
<a name="net-dg-hosm-run-the-app"></a>

1. 将应用程序从本地驱动器复制到您的实例。

   传输哪些文件取决于您如何构建应用程序以及您的实例是否安装了 .NET Core 运行时系统。有关如何将文件传输到实例的信息，请参阅 [Amazon EC2 用户指南](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)中的[连接到 Linux 实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html)（请参阅相应的子部分）或[将文件传输到 Windows 实例](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instanceWindowsFileTransfer.html)。

1. 启动应用程序并验证其运行结果是否与开发计算机上的结果相同。

1. 验证应用程序是否使用 IAM 角色提供的凭证。

   1. 打开 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/)。

   1. 选择实例，然后通过**操作**、**实例设置**和**附加/替换 IAM 角色**分离 IAM 角色。

   1. 再次运行该应用程序，看看它是否返回了授权错误。

## 清理
<a name="net-dg-hosm-cleanup"></a>

完成本教程后，如果您不再需要自己创建的 EC2 实例，请务必终止该实例以避免不必要的开支。您可以在 [Amazon EC2 控制台](https://console.aws.amazon.com/ec2/)中或以编程方式执行此操作，如[终止 Amazon EC2 实例](terminate-instance.md)中所述。您还可以删除为本教程创建的其它资源。其中可能包括 IAM 角色、EC2 密钥对和 PEM 文件、安全组等。

# 使用 Amazon Simple Storage Service Internet 存储
<a name="s3-apis-intro"></a>

 适用于 .NET 的 AWS SDK 支持用于互联网存储的 [Amazon S3](https://aws.amazon.com/s3/)。该服务旨在降低开发人员进行网络规模级计算的难度。

## APIs
<a name="w2aac19c15c21b5"></a>

针 适用于 .NET 的 AWS SDK 对 Amazon S3 客户端。 APIs APIs 使您能够使用 Amazon S3 资源，例如存储桶和项目。要查看 Amazon S3 APIs 的完整套件，请参阅以下内容：
+ [适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)（然后滚动至 “Amazon.S3”）。
+ [Amazon.Extensions.S3.Encryption](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.html) 文档

Amazon S3 APIs 由以下 NuGet 软件包提供：
+ [AWSSDK.S3](https://www.nuget.org/packages/AWSSDK.S3)
+ [Amazon.Extensions.S3.Encryption](https://www.nuget.org/packages/Amazon.Extensions.S3.Encryption)

## 先决条件
<a name="w2aac19c15c21b7"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

## 本文档中的示例
<a name="s3-apis-examples"></a>

本文档中的以下主题向您展示了如何使用与 Amazon S3 配合使用。 适用于 .NET 的 AWS SDK 
+ [使用 KMS 密钥进行 S3 加密](kms-keys-s3-encryption.md)

## 其它文档中的示例
<a name="s3-apis-examples-other"></a>

以下指[向 Amazon S3 开发者指南](https://docs.aws.amazon.com/AmazonS3/latest/userguide/)的链接提供了更多示例，说明如何使用与 Amazon S3 配合使用。 适用于 .NET 的 AWS SDK 

**注意**  
尽管这些示例和其他编程注意事项是为 适用于 .NET 的 AWS SDK 使用.NET Framework 的版本 3 创建的，但它们也适用于 适用于 .NET 的 AWS SDK 使用.NET Core 的更高版本。有时需要对代码进行细微调整。

**Amazon S3 编程示例**
+  [管理 ACLs](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-using-dot-net-sdk.html) 
+  [创建存储桶](https://docs.aws.amazon.com/AmazonS3/latest/dev/create-bucket-get-location-example.html#create-bucket-get-location-dotnet) 
+  [上传对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/UploadObjSingleOpNET.html) 
+  使用@@ [高级别 API ([Amazon.S3.Transfer) 进行分段上传。 TransferUtility](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TTransferUtility.html)](https://docs.aws.amazon.com/AmazonS3/latest/dev/usingHLmpuDotNet.html)) 
+  [使用低级别 API 的分段上传](https://docs.aws.amazon.com/AmazonS3/latest/dev/usingLLmpuDotNet.html) 
+  [列出对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/list-obj-version-enabled-bucket.html#list-obj-version-enabled-bucket-sdk-examples) 
+  [列出密钥](https://docs.aws.amazon.com/AmazonS3/latest/dev/ListingObjectKeysUsingNetSDK.html) 
+  [获取对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/RetrievingObjectUsingNetSDK.html) 
+  [复制对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjectUsingNetSDK.html) 
+  [使用分段上传 API 复制对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjctsUsingLLNetMPUapi.html) 
+  [删除对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/DeletingOneObjectUsingNetSDK.html) 
+  [删除多个对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/DeletingMultipleObjectsUsingNetSDK.html) 
+  [恢复对象](https://docs.aws.amazon.com/AmazonS3/latest/dev/restore-object-dotnet.html) 
+  [为通知配置存储桶](https://docs.aws.amazon.com/AmazonS3/latest/dev/ways-to-add-notification-config-to-bucket.html) 
+  [管理对象的生命周期](https://docs.aws.amazon.com/AmazonS3/latest/dev/manage-lifecycle-using-dot-net.html) 
+  [生成预签名对象 URL](https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLDotNetSDK.html) 
+  [管理网站](https://docs.aws.amazon.com/AmazonS3/latest/dev/ConfigWebSiteDotNet.html) 
+  [允许跨源资源共享 (CORS)](https://docs.aws.amazon.com/AmazonS3/latest/dev/ManageCorsUsingDotNet.html) 

**其它编程注意事项**
+  [使用 适用于 .NET 的 AWS SDK 进行 Amazon S3 编程](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingTheMPDotNetAPI.html) 
+  [使用 IAM 用户临时证书创建请求](https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempSessionTokenDotNet.html) 
+  [使用联合身份用户临时凭证创建请求](https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempFederationTokenDotNet.html) 
+  [指定服务器端加密](https://docs.aws.amazon.com/AmazonS3/latest/dev/SSEUsingDotNetSDK.html) 
+  [使用客户提供的加密密钥指定服务器端加密](https://docs.aws.amazon.com/AmazonS3/latest/dev/sse-c-using-dot-net-sdk.html) 

# 在中使用 AWS KMS 密钥进行 Amazon S3 加密 适用于 .NET 的 AWS SDK
<a name="kms-keys-s3-encryption"></a>

此示例向您展示如何使用 AWS Key Management Service 密钥加密 Amazon S3 对象。该应用程序创建客户主密钥 (CMK)，并使用它来创建用于客户端加密的 A [mazonS3 EncryptionClient V2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3EncryptionClientV2.html) 对象。应用程序使用该客户端根据现有 Amazon S3 桶中的给定文本文件创建加密对象。然后，它会解密对象并显示其内容。

**警告**  
名为`AmazonS3EncryptionClient`的类似类已被弃用，其安全性不如 `AmazonS3EncryptionClientV2` 类。要迁移使用 `AmazonS3EncryptionClient` 的现有代码，请参阅[S3 加密客户端迁移（从 V1 到 V2）](s3-encryption-migration-v1-v2.md)。

**Topics**
+ [创建加密材料](#kms-s3-enc-mat)
+ [创建并加密 Amazon S3 对象](#kms-s3-create-ojbect)
+ [完整代码](#kms-s3-complete-code)
+ [其他注意事项](#kms-s3-additional)

## 创建加密材料
<a name="kms-s3-enc-mat"></a>

以下代码片段创建了一个包含 KMS 密钥 ID 的 `EncryptionMaterials` 对象。

[本主题末尾](#kms-s3-complete-code)的示例显示了此片段的使用情况。

```
      // Create a customer master key (CMK) and store the result
      CreateKeyResponse createKeyResponse =
        await new AmazonKeyManagementServiceClient().CreateKeyAsync(new CreateKeyRequest());
      var kmsEncryptionContext = new Dictionary<string, string>();
      var kmsEncryptionMaterials = new EncryptionMaterialsV2(
        createKeyResponse.KeyMetadata.KeyId, KmsType.KmsContext, kmsEncryptionContext);
```

## 创建并加密 Amazon S3 对象
<a name="kms-s3-create-ojbect"></a>

以下代码片段创建了一个使用先前创建的加密材料的 `AmazonS3EncryptionClientV2` 对象。然后，它使用客户端来创建和加密一个新的 Amazon S3 对象。

[本主题末尾](#kms-s3-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to create and encrypt an object in an S3 bucket
    static async Task<GetObjectResponse> CreateAndRetrieveObjectAsync(
      EncryptionMaterialsV2 materials, string bucketName,
      string fileName, string itemName)
    {
      // CryptoStorageMode.ObjectMetadata is required for KMS EncryptionMaterials
      var config = new AmazonS3CryptoConfigurationV2(SecurityProfile.V2AndLegacy)
      {
        StorageMode = CryptoStorageMode.ObjectMetadata
      };
      var s3EncClient = new AmazonS3EncryptionClientV2(config, materials);

      // Create, encrypt, and put the object
      await s3EncClient.PutObjectAsync(new PutObjectRequest
      {
        BucketName = bucketName,
        Key = itemName,
        ContentBody = File.ReadAllText(fileName)
      });

      // Get, decrypt, and return the object
      return await s3EncClient.GetObjectAsync(new GetObjectRequest
      {
        BucketName = bucketName,
        Key = itemName
      });
    }
```

## 完整代码
<a name="kms-s3-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c21c13c15b5b1"></a>

NuGet 包裹：
+ [Amazon.Extensions.S3.Encryption](https://www.nuget.org/packages/Amazon.Extensions.S3.Encryption)

编程元素：
+ 命名空间 [Amazon.Extensions.S3.Encryption](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.html)

  [AmazonS3 EncryptionClient](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3EncryptionClientV2.html) 级 V2

  [AmazonS3 CryptoConfiguration](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3CryptoConfigurationV2.html) 级 V2

  班级 [CryptoStorageMode](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.CryptoStorageMode.html)

  [EncryptionMaterialsV](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.EncryptionMaterialsV2.html) 2 级
+ 命名空间 [Amazon.Extensions.S3.Encryption.Primitives](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.Primitives.html)

  班级 [KmsType](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.Primitives.KmsType.html)
+ 命名空间 [Amazon.S3.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/NS3Model.html)

  班级 [GetObjectRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TGetObjectRequest.html)

  班级 [GetObjectResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TGetObjectResponse.html)

  班级 [PutObjectRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/S3/TPutObjectRequest.html)
+ 命名空间 [Amazon。 KeyManagementService](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/KeyManagementService/NKeyManagementService.html)

  班级 [AmazonKeyManagementServiceClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/KeyManagementService/TKeyManagementServiceClient.html)
+ 命名空间 [Amazon。 KeyManagementService.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/KeyManagementService/NKeyManagementServiceModel.html)

  班级 [CreateKeyRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/KeyManagementService/TCreateKeyRequest.html)

  班级 [CreateKeyResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/KeyManagementService/TCreateKeyResponse.html)

### 代码
<a name="w2aac19c15c21c13c15b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.Extensions.S3.Encryption;
using Amazon.Extensions.S3.Encryption.Primitives;
using Amazon.S3.Model;
using Amazon.KeyManagementService;
using Amazon.KeyManagementService.Model;

namespace KmsS3Encryption
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to store text in an encrypted S3 object.
  class Program
  {
    private const int MaxArgs = 3;

    public static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if((parsedArgs.Count == 0) || (parsedArgs.Count > MaxArgs))
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string bucketName =
        CommandLine.GetArgument(parsedArgs, null, "-b", "--bucket-name");
      string fileName =
        CommandLine.GetArgument(parsedArgs, null, "-f", "--file-name");
      string itemName =
        CommandLine.GetArgument(parsedArgs, null, "-i", "--item-name");
      if(string.IsNullOrEmpty(bucketName) || (string.IsNullOrEmpty(fileName)))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");
      if(!File.Exists(fileName))
        CommandLine.ErrorExit($"\nThe given file {fileName} doesn't exist.");
      if(string.IsNullOrEmpty(itemName))
        itemName = Path.GetFileName(fileName);

      // Create a customer master key (CMK) and store the result
      CreateKeyResponse createKeyResponse =
        await new AmazonKeyManagementServiceClient().CreateKeyAsync(new CreateKeyRequest());
      var kmsEncryptionContext = new Dictionary<string, string>();
      var kmsEncryptionMaterials = new EncryptionMaterialsV2(
        createKeyResponse.KeyMetadata.KeyId, KmsType.KmsContext, kmsEncryptionContext);

      // Create the object in the bucket, then display the content of the object
      var putObjectResponse =
        await CreateAndRetrieveObjectAsync(kmsEncryptionMaterials, bucketName, fileName, itemName);
      Stream stream = putObjectResponse.ResponseStream;
      StreamReader reader = new StreamReader(stream);
      Console.WriteLine(reader.ReadToEnd());
    }


    //
    // Method to create and encrypt an object in an S3 bucket
    static async Task<GetObjectResponse> CreateAndRetrieveObjectAsync(
      EncryptionMaterialsV2 materials, string bucketName,
      string fileName, string itemName)
    {
      // CryptoStorageMode.ObjectMetadata is required for KMS EncryptionMaterials
      var config = new AmazonS3CryptoConfigurationV2(SecurityProfile.V2AndLegacy)
      {
        StorageMode = CryptoStorageMode.ObjectMetadata
      };
      var s3EncClient = new AmazonS3EncryptionClientV2(config, materials);

      // Create, encrypt, and put the object
      await s3EncClient.PutObjectAsync(new PutObjectRequest
      {
        BucketName = bucketName,
        Key = itemName,
        ContentBody = File.ReadAllText(fileName)
      });

      // Get, decrypt, and return the object
      return await s3EncClient.GetObjectAsync(new GetObjectRequest
      {
        BucketName = bucketName,
        Key = itemName
      });
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: KmsS3Encryption -b <bucket-name> -f <file-name> [-i <item-name>]" +
        "\n  -b, --bucket-name: The name of an existing S3 bucket." +
        "\n  -f, --file-name: The name of a text file with content to encrypt and store in S3." +
        "\n  -i, --item-name: The name you want to use for the item." +
        "\n      If item-name isn't given, file-name will be used.");
    }

  }

  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="kms-s3-additional"></a>
+ 您可以查看此示例的结果。为此，请前往 [Amazon S3 控制台](https://console.aws.amazon.com/s3)并打开您提供给应用程序的桶。然后找到新对象，下载后在文本编辑器中打开该对象。
+ A [mazonS3 EncryptionClient V2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3EncryptionClientV2.html) 类实现的接口与标准`AmazonS3Client`类相同。这样可以更轻松地将代码移植到 `AmazonS3EncryptionClientV2` 类中，从而在客户端中自动透明地进行加密和解密。
+ 使用 AWS KMS 密钥作为主密钥的一个好处是，您无需存储和管理自己的主密钥；这是通过完成的 AWS。第二个优点是，的`AmazonS3EncryptionClientV2` 适用于 .NET 的 AWS SDK 类可以与的`AmazonS3EncryptionClientV2`类互操作。 适用于 Java 的 AWS SDK这意味着您可以使用加密 适用于 Java 的 AWS SDK 并使用解密 适用于 .NET 的 AWS SDK，反之亦然。
**注意**  
的`AmazonS3EncryptionClientV2`类仅在元数据模式下运行时 适用于 .NET 的 AWS SDK 支持 KMS 主密钥。的`AmazonS3EncryptionClientV2`类的指令文件模式与的`AmazonS3EncryptionClientV2`类 适用于 .NET 的 AWS SDK 不兼容 适用于 Java 的 AWS SDK。
+ 有关该`AmazonS3EncryptionClientV2`类的客户端加密以及信封加密的工作原理的更多信息，请参阅使用[适用于 .NET 的 AWS SDK 和 Amazon S3 进行客户端数据加密](https://aws.amazon.com/blogs/developer/client-side-data-encryption-with-aws-sdk-for-net-and-amazon-s3/)。

# 使用 Amazon Simple Notification Service 从云端发送通知
<a name="sns-apis-intro"></a>

**注意**  
本主题中的信息特定于基于.NET Framework 和 3.3 及更早 适用于 .NET 的 AWS SDK 版本的项目。

 适用于 .NET 的 AWS SDK 支持亚马逊简单通知服务 (Amazon SNS) Simple Notification Service，这是一项网络服务，使应用程序、最终用户和设备能够立即从云端发送通知。有关更多信息，请参阅 [Amazon SNS](https://aws.amazon.com/sns/)。

## 列出您的 Amazon SNS 话题
<a name="sns-list-example"></a>

以下示例说明如何列出您的 Amazon SNS 主题、每个主题的订阅以及每个主题的属性。此示例使用默认值[AmazonSimpleNotificationServiceClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SNS/TSNSClient.html)。

```
// using Amazon.SimpleNotificationService;
// using Amazon.SimpleNotificationService.Model;

var client = new AmazonSimpleNotificationServiceClient();
var request = new ListTopicsRequest();
var response = new ListTopicsResponse();

do
{
  response = client.ListTopics(request);

  foreach (var topic in response.Topics)
  {
    Console.WriteLine("Topic: {0}", topic.TopicArn);

    var subs = client.ListSubscriptionsByTopic(
      new ListSubscriptionsByTopicRequest
      {
        TopicArn = topic.TopicArn
      });

    var ss = subs.Subscriptions;

    if (ss.Any())
    {
      Console.WriteLine("  Subscriptions:");

      foreach (var sub in ss)
      {
        Console.WriteLine("    {0}", sub.SubscriptionArn);
      }
    }

    var attrs = client.GetTopicAttributes(
      new GetTopicAttributesRequest
      {
        TopicArn = topic.TopicArn
      }).Attributes;

    if (attrs.Any())
    {
      Console.WriteLine("  Attributes:");

      foreach (var attr in attrs)
      {
        Console.WriteLine("    {0} = {1}", attr.Key, attr.Value);
      }
    }

    Console.WriteLine();
  }

  request.NextToken = response.NextToken;

} while (!string.IsNullOrEmpty(response.NextToken));
```

## 将消息发送到 Amazon SNS 主题
<a name="sns-send-message-example"></a>

以下代码示例显示如何将消息发送到 Amazon SNS 主题中。该示例采用了一个参数，即 Amazon SNS 主题的 ARN。

```
using System;
using System.Linq;
using System.Threading.Tasks;

using Amazon;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;

namespace SnsSendMessage
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Topic ARNs must be in the correct format:
             *   arn:aws:sns:REGION:ACCOUNT_ID:NAME
             *
             *  where:
             *  REGION     is the region in which the topic is created, such as us-west-2
             *  ACCOUNT_ID is your (typically) 12-character account ID
             *  NAME       is the name of the topic
             */
            string topicArn = args[0];
            string message = "Hello at " + DateTime.Now.ToShortTimeString();

            var client = new AmazonSimpleNotificationServiceClient(region: Amazon.RegionEndpoint.USWest2);

            var request = new PublishRequest
            {
                Message = message,
                TopicArn = topicArn
            };

            try
            {
                var response = client.Publish(request);

                Console.WriteLine("Message sent to topic:");
                Console.WriteLine(message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught exception publishing request:");
                Console.WriteLine(ex.Message);
            }
        }
    }
}
```

请参阅[完整的示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/.dotnet/example_code_legacy/SNS/SnsSendMessage.cs)，包括有关如何从命令行构建和运行该示例的信息 GitHub。

## 向一个电话号码发送 SMS 消息
<a name="sns-send-sms-example"></a>

以下示例说明如何向电话号码发送 SMS 消息。该示例采用一个参数，即电话号码，该参数必须采用注释中描述的两种格式中的任何一种。

```
using System;
using System.Linq;
using System.Threading.Tasks;
using Amazon;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;

namespace SnsPublish
{
    class Program
    {
        static void Main(string[] args)
        {
            // US phone numbers must be in the correct format:
            // +1 (nnn) nnn-nnnn OR +1nnnnnnnnnn
            string number = args[0];
            string message = "Hello at " + DateTime.Now.ToShortTimeString();

            var client = new AmazonSimpleNotificationServiceClient(region: Amazon.RegionEndpoint.USWest2);
            var request = new PublishRequest
            {
                Message = message,
                PhoneNumber = number
            };

            try
            {
                var response = client.Publish(request);

                Console.WriteLine("Message sent to " + number + ":");
                Console.WriteLine(message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught exception publishing request:");
                Console.WriteLine(ex.Message);
            }
        }
    }
}
```

请参阅[完整的示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/.dotnet/example_code_legacy/SNS/SnsPublish.cs)，包括有关如何从命令行构建和运行该示例的信息 GitHub。

# 使用 Amazon SQS 发送消息
<a name="sqs-apis-intro"></a>

 适用于 .NET 的 AWS SDK 支持[亚马逊简单队列服务 (Amazon SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/)) Simple Queue Service，这是一项消息队列服务，用于处理系统中组件之间的消息或工作流程。

Amazon SQS 队列提供了一种机制，使您能够在微服务、分布式系统和无服务器应用程序等软件组件之间发送、存储和接收消息。这使您能够分离此类组件，无需设计和操作自己的消息传递系统。有关 Amazon SQS 中队列和消息的工作原理的信息，请参阅 [Amazon Simple Queue Service 开发人员指南](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/)中的 [Amazon SQS 教程](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-other-tutorials.html)和[基本 Amazon SQS 架构](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-architecture.html)。

**重要**  
由于队列的分布式特性，Amazon SQS 无法保证您以消息发送的准确顺序接收消息。如果您需要保留消息顺序，请使用 [Amazon SQS FIFO 队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-fifo-queues.html)。

## APIs
<a name="w2aac19c15c25b9"></a>

 APIs 为亚马逊 SQS 客户 适用于 .NET 的 AWS SDK 提供服务。 APIs 使您可以使用 Amazon SQS 功能，例如队列和消息。本节包含少量示例，向您展示使用这些示例时可以遵循的模式 APIs。要查看全套内容 APIs，请参阅 [适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)（并滚动至 “Amazon.sqs”）。

亚马逊 SQS APIs 由 [AWSSDK.](https://www.nuget.org/packages/AWSSDK.SQS) NuGet SQS 软件包提供。

## 先决条件
<a name="w2aac19c15c25c11"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

## 主题
<a name="w2aac19c15c25c13"></a>

**Topics**
+ [APIs](#w2aac19c15c25b9)
+ [先决条件](#w2aac19c15c25c11)
+ [主题](#w2aac19c15c25c13)
+ [创建队列](CreateQueue.md)
+ [更新队列](UpdateSqsQueue.md)
+ [删除队列](DeleteSqsQueue.md)
+ [发送消息](SendMessage.md)
+ [接收消息](ReceiveMessage.md)

# 创建 Amazon SQS 队列
<a name="CreateQueue"></a>

此示例向您展示如何使用创建 Amazon SQS 队列。 适用于 .NET 的 AWS SDK 如果您不提供[死信队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html)的 ARN，则应用程序会创建一个死信队列。然后，它会创建一个标准消息队列，其中包括死信队列（您提供的队列或创建的队列）。

如果您不提供任何命令行参数，则应用程序仅显示有关所有现有队列的信息。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#CreateQueue-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [显示现有队列](#CreateQueue-show-queues)
+ [创建队列](#CreateQueue-create-queue)
+ [获取队列的 ARN](#CreateQueue-get-arn)
+ [完整代码](#CreateQueue-complete-code)
+ [其他注意事项](#CreateQueue-additional)

## 显示现有队列
<a name="CreateQueue-show-queues"></a>

以下代码片段显示了 SQS 客户端区域中现有队列的列表以及每个队列的属性。

[本主题末尾](#CreateQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to show a list of the existing queues
    private static async Task ShowQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine();
      foreach(string qUrl in responseList.QueueUrls)
      {
        // Get and show all attributes. Could also get a subset.
        await ShowAllAttributes(sqsClient, qUrl);
      }
    }

    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      var attributes = new List<string>{ QueueAttributeName.All };
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl, attributes);
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }
```

## 创建队列
<a name="CreateQueue-create-queue"></a>

以下代码片段创建队列。该片段包括死信队列的使用，但队列不一定需要死信队列。

[本主题末尾](#CreateQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to create a queue. Returns the queue URL.
    private static async Task<string> CreateQueue(
      IAmazonSQS sqsClient, string qName, string deadLetterQueueUrl=null,
      string maxReceiveCount=null, string receiveWaitTime=null)
    {
      var attrs = new Dictionary<string, string>();

      // If a dead-letter queue is given, create a message queue
      if(!string.IsNullOrEmpty(deadLetterQueueUrl))
      {
        attrs.Add(QueueAttributeName.ReceiveMessageWaitTimeSeconds, receiveWaitTime);
        attrs.Add(QueueAttributeName.RedrivePolicy,
          $"{{\"deadLetterTargetArn\":\"{await GetQueueArn(sqsClient, deadLetterQueueUrl)}\"," +
          $"\"maxReceiveCount\":\"{maxReceiveCount}\"}}");
        // Add other attributes for the message queue such as VisibilityTimeout
      }

      // If no dead-letter queue is given, create one of those instead
      //else
      //{
      //  // Add attributes for the dead-letter queue as needed
      //  attrs.Add();
      //}

      // Create the queue
      CreateQueueResponse responseCreate = await sqsClient.CreateQueueAsync(
          new CreateQueueRequest{QueueName = qName, Attributes = attrs});
      return responseCreate.QueueUrl;
    }
```

## 获取队列的 ARN
<a name="CreateQueue-get-arn"></a>

以下代码片段获取由给定队列 URL 标识的队列的 ARN。

[本主题末尾](#CreateQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to get the ARN of a queue
    private static async Task<string> GetQueueArn(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt = await sqsClient.GetQueueAttributesAsync(
        qUrl, new List<string>{QueueAttributeName.QueueArn});
      return responseGetAtt.QueueARN;
    }
```

## 完整代码
<a name="CreateQueue-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c25c17c25b5b1"></a>

NuGet 包裹：
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

编程元素：
+ 命名空间 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQS.html)

  [Amazon 上](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html)课 SQSClient

  班级 [QueueAttributeName](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TQueueAttributeName.html)
+ 命名空间 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQSModel.html)

  班级 [CreateQueueRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TCreateQueueRequest.html)

  班级 [CreateQueueResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TCreateQueueResponse.html)

  班级 [GetQueueAttributesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TGetQueueAttributesResponse.html)

  班级 [ListQueuesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TListQueuesResponse.html)

### 代码
<a name="w2aac19c15c25c17c25b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSCreateQueue
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create a queue
  class Program
  {
    private const string MaxReceiveCount = "10";
    private const string ReceiveMessageWaitTime = "2";
    private const int MaxArgs = 3;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count > MaxArgs)
        CommandLine.ErrorExit(
          "\nToo many command-line arguments.\nRun the command with no arguments to see help.");

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // In the case of no command-line arguments, just show help and the existing queues
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        Console.WriteLine("\nNo arguments specified.");
        Console.Write("Do you want to see a list of the existing queues? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await ShowQueues(sqsClient);
        return;
      }

      // Get the application arguments from the parsed list
      string queueName =
        CommandLine.GetArgument(parsedArgs, null, "-q", "--queue-name");
      string deadLetterQueueUrl =
        CommandLine.GetArgument(parsedArgs, null, "-d", "--dead-letter-queue");
      string maxReceiveCount =
        CommandLine.GetArgument(parsedArgs, MaxReceiveCount, "-m", "--max-receive-count");
      string receiveWaitTime =
        CommandLine.GetArgument(parsedArgs, ReceiveMessageWaitTime, "-w", "--wait-time");

      if(string.IsNullOrEmpty(queueName))
        CommandLine.ErrorExit(
          "\nYou must supply a queue name.\nRun the command with no arguments to see help.");

      // If a dead-letter queue wasn't given, create one
      if(string.IsNullOrEmpty(deadLetterQueueUrl))
      {
        Console.WriteLine("\nNo dead-letter queue was specified. Creating one...");
        deadLetterQueueUrl = await CreateQueue(sqsClient, queueName + "__dlq");
        Console.WriteLine($"Your new dead-letter queue:");
        await ShowAllAttributes(sqsClient, deadLetterQueueUrl);
      }

      // Create the message queue
      string messageQueueUrl = await CreateQueue(
        sqsClient, queueName, deadLetterQueueUrl, maxReceiveCount, receiveWaitTime);
      Console.WriteLine($"Your new message queue:");
      await ShowAllAttributes(sqsClient, messageQueueUrl);
    }


    //
    // Method to show a list of the existing queues
    private static async Task ShowQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine();
      foreach(string qUrl in responseList.QueueUrls)
      {
        // Get and show all attributes. Could also get a subset.
        await ShowAllAttributes(sqsClient, qUrl);
      }
    }


    //
    // Method to create a queue. Returns the queue URL.
    private static async Task<string> CreateQueue(
      IAmazonSQS sqsClient, string qName, string deadLetterQueueUrl=null,
      string maxReceiveCount=null, string receiveWaitTime=null)
    {
      var attrs = new Dictionary<string, string>();

      // If a dead-letter queue is given, create a message queue
      if(!string.IsNullOrEmpty(deadLetterQueueUrl))
      {
        attrs.Add(QueueAttributeName.ReceiveMessageWaitTimeSeconds, receiveWaitTime);
        attrs.Add(QueueAttributeName.RedrivePolicy,
          $"{{\"deadLetterTargetArn\":\"{await GetQueueArn(sqsClient, deadLetterQueueUrl)}\"," +
          $"\"maxReceiveCount\":\"{maxReceiveCount}\"}}");
        // Add other attributes for the message queue such as VisibilityTimeout
      }

      // If no dead-letter queue is given, create one of those instead
      //else
      //{
      //  // Add attributes for the dead-letter queue as needed
      //  attrs.Add();
      //}

      // Create the queue
      CreateQueueResponse responseCreate = await sqsClient.CreateQueueAsync(
          new CreateQueueRequest{QueueName = qName, Attributes = attrs});
      return responseCreate.QueueUrl;
    }


    //
    // Method to get the ARN of a queue
    private static async Task<string> GetQueueArn(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt = await sqsClient.GetQueueAttributesAsync(
        qUrl, new List<string>{QueueAttributeName.QueueArn});
      return responseGetAtt.QueueARN;
    }


    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      var attributes = new List<string>{ QueueAttributeName.All };
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl, attributes);
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
      "\nUsage: SQSCreateQueue -q <queue-name> [-d <dead-letter-queue>]" +
        " [-m <max-receive-count>] [-w <wait-time>]" +
      "\n  -q, --queue-name: The name of the queue you want to create." +
      "\n  -d, --dead-letter-queue: The URL of an existing queue to be used as the dead-letter queue."+
      "\n      If this argument isn't supplied, a new dead-letter queue will be created." +
      "\n  -m, --max-receive-count: The value for maxReceiveCount in the RedrivePolicy of the queue." +
      $"\n      Default is {MaxReceiveCount}." +
      "\n  -w, --wait-time: The value for ReceiveMessageWaitTimeSeconds of the queue for long polling." +
      $"\n      Default is {ReceiveMessageWaitTime}.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="CreateQueue-additional"></a>
+ 您的队列名称必须由字母数字字符、连字符和下划线组成。
+ 队列名称和队列区 URLs 分大小写
+ 如果您需要队列 URL 但只有队列名称，请使用其中一种 `AmazonSQSClient.GetQueueUrlAsync` 方法。
+ 有关您可以设置的各种队列属性的信息，请参阅 [CreateQueueRequest 适用于 .NET 的 AWS SDK](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TCreateQueueRequest.html)[API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)或[SetQueueAttributes](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SetQueueAttributes.html)《[亚马逊简单队列服务 API 参考](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/)》。
+ 此示例指定对您创建的队列中的所有消息进行长轮询。可使用 `ReceiveMessageWaitTimeSeconds` 属性执行此操作。

  您还可以在调用 [Amazon SQSClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html) 类的`ReceiveMessageAsync`方法期间指定长轮询。有关更多信息，请参阅 [接收 Amazon SQS 消息](ReceiveMessage.md)。

  有关短轮询与长轮询的信息，请参阅《Amazon Simple Queue Service 开发人员指南》**中的[短轮询与长轮询](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html)。
+ 其它（源）队列可将未成功处理的消息转到死信队列。有关更多信息，请参阅《Amazon Simple Queue Service 开发人员指南》中的 [Amazon SQS 死信队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html)。
+ 您还可以在 [Amazon SQS 控制台](https://console.aws.amazon.com/sqs)中查看队列列表和此示例的结果。

# 更新 Amazon SQS 队列
<a name="UpdateSqsQueue"></a>

此示例向您展示如何使用更新 Amazon SQS 队列。 适用于 .NET 的 AWS SDK 经过一些检查后，应用程序使用给定值更新给定属性，然后显示队列的所有属性。

如果命令行参数中仅包含队列 URL，则应用程序仅显示队列的所有属性。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#UpdateSqsQueue-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [显示队列属性](#UpdateSqsQueue-show-attributes)
+ [验证属性名称](#UpdateSqsQueue-validate-attribute)
+ [更新队列属性](#UpdateSqsQueue-update-attribute)
+ [完整代码](#UpdateSqsQueue-complete-code)
+ [其他注意事项](#UpdateSqsQueue-additional)

## 显示队列属性
<a name="UpdateSqsQueue-show-attributes"></a>

以下代码片段显示了由给定队列 URL 标识的队列的属性。

[本主题末尾](#UpdateSqsQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl,
          new List<string>{ QueueAttributeName.All });
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }
```

## 验证属性名称
<a name="UpdateSqsQueue-validate-attribute"></a>

以下代码片段验证了正在更新的属性的名称。

[本主题末尾](#UpdateSqsQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to check the name of the attribute
    private static bool ValidAttribute(string attribute)
    {
      var attOk = false;
      var qAttNameType = typeof(QueueAttributeName);
      List<string> qAttNamefields = new List<string>();
      foreach(var field in qAttNameType.GetFields())
       qAttNamefields.Add(field.Name);
      foreach(var name in qAttNamefields)
        if(attribute == name) { attOk = true; break; }
      return attOk;
    }
```

## 更新队列属性
<a name="UpdateSqsQueue-update-attribute"></a>

以下代码片段更新了由给定队列 URL 标识的队列属性。

[本主题末尾](#UpdateSqsQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to update a queue attribute
    private static async Task UpdateAttribute(
      IAmazonSQS sqsClient, string qUrl, string attribute, string value)
    {
      await sqsClient.SetQueueAttributesAsync(qUrl,
        new Dictionary<string, string>{{attribute, value}});
    }
```

## 完整代码
<a name="UpdateSqsQueue-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c25c19c25b5b1"></a>

NuGet 包裹：
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

编程元素：
+ 命名空间 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQS.html)

  [Amazon 上](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html)课 SQSClient

  班级 [QueueAttributeName](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TQueueAttributeName.html)
+ 命名空间 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQSModel.html)

  班级 [GetQueueAttributesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TGetQueueAttributesResponse.html)

### 代码
<a name="w2aac19c15c25c19c25b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSUpdateQueue
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to update a queue
  class Program
  {
    private const int MaxArgs = 3;
    private const int InvalidArgCount = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }
      if((parsedArgs.Count > MaxArgs) || (parsedArgs.Count == InvalidArgCount))
        CommandLine.ErrorExit("\nThe number of command-line arguments is incorrect." +
          "\nRun the command with no arguments to see help.");

      // Get the application arguments from the parsed list
      var qUrl = CommandLine.GetArgument(parsedArgs, null, "-q");
      var attribute = CommandLine.GetArgument(parsedArgs, null, "-a");
      var value = CommandLine.GetArgument(parsedArgs, null, "-v", "--value");

      if(string.IsNullOrEmpty(qUrl))
        CommandLine.ErrorExit("\nYou must supply at least a queue URL." +
          "\nRun the command with no arguments to see help.");

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // In the case of one command-line argument, just show the attributes for the queue
      if(parsedArgs.Count == 1)
        await ShowAllAttributes(sqsClient, qUrl);

      // Otherwise, attempt to update the given queue attribute with the given value
      else
      {
        // Check to see if the attribute is valid
        if(ValidAttribute(attribute))
        {
          // Perform the update and then show all the attributes of the queue
          await UpdateAttribute(sqsClient, qUrl, attribute, value);
          await ShowAllAttributes(sqsClient, qUrl);
        }
        else
        {
          Console.WriteLine($"\nThe given attribute name, {attribute}, isn't valid.");
        }
      }
    }


    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl,
          new List<string>{ QueueAttributeName.All });
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }


    //
    // Method to check the name of the attribute
    private static bool ValidAttribute(string attribute)
    {
      var attOk = false;
      var qAttNameType = typeof(QueueAttributeName);
      List<string> qAttNamefields = new List<string>();
      foreach(var field in qAttNameType.GetFields())
       qAttNamefields.Add(field.Name);
      foreach(var name in qAttNamefields)
        if(attribute == name) { attOk = true; break; }
      return attOk;
    }


    //
    // Method to update a queue attribute
    private static async Task UpdateAttribute(
      IAmazonSQS sqsClient, string qUrl, string attribute, string value)
    {
      await sqsClient.SetQueueAttributesAsync(qUrl,
        new Dictionary<string, string>{{attribute, value}});
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine("\nUsage: SQSUpdateQueue -q queue_url [-a attribute -v value]");
      Console.WriteLine("  -q: The URL of the queue you want to update.");
      Console.WriteLine("  -a: The name of the attribute to update.");
      Console.WriteLine("  -v, --value: The value to assign to the attribute.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 其他注意事项
<a name="UpdateSqsQueue-additional"></a>
+ 要更新该`RedrivePolicy`属性，您必须根据自己的操作系统将整个值加上引号并 key/value 对对的引号进行转义。

  例如，在 Windows 上，该值的构造方式类似于以下内容：

  ```
  "{\"deadLetterTargetArn\":\"DEAD_LETTER-QUEUE-ARN\",\"maxReceiveCount\":\"10\"}"
  ```

# 删除 Amazon SQS 队列
<a name="DeleteSqsQueue"></a>

此示例向您展示如何使用删除 Amazon SQS 队列。 适用于 .NET 的 AWS SDK 应用程序删除队列，等到队列消失，然后显示剩余队列的列表。

如果您不提供任何命令行参数，则应用程序仅显示现有队列的列表。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#DeleteSqsQueue-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [删除队列](#DeleteSqsQueue-delete-queue)
+ [等待队列消失](#DeleteSqsQueue-wait)
+ [显示现有队列的列表](#DeleteSqsQueue-list-queues)
+ [完整代码](#DeleteSqsQueue-complete-code)
+ [其他注意事项](#DeleteSqsQueue-additional)

## 删除队列
<a name="DeleteSqsQueue-delete-queue"></a>

以下代码片段删除了由给定队列 URL 标识的队列。

[本主题末尾](#DeleteSqsQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to delete an SQS queue
    private static async Task DeleteQueue(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"Deleting queue {qUrl}...");
      await sqsClient.DeleteQueueAsync(qUrl);
      Console.WriteLine($"Queue {qUrl} has been deleted.");
    }
```

## 等待队列消失
<a name="DeleteSqsQueue-wait"></a>

以下代码片段等待删除过程完成，这可能需要 60 秒。

[本主题末尾](#DeleteSqsQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to wait up to a given number of seconds
    private static async Task Wait(
      IAmazonSQS sqsClient, int numSeconds, string qUrl)
    {
      Console.WriteLine($"Waiting for up to {numSeconds} seconds.");
      Console.WriteLine("Press any key to stop waiting. (Response might be slightly delayed.)");
      for(int i=0; i<numSeconds; i++)
      {
        Console.Write(".");
        Thread.Sleep(1000);
        if(Console.KeyAvailable) break;

        // Check to see if the queue is gone yet
        var found = false;
        ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
        foreach(var url in responseList.QueueUrls)
        {
          if(url == qUrl)
          {
            found = true;
            break;
          }
        }
        if(!found) break;
      }
    }
```

## 显示现有队列的列表
<a name="DeleteSqsQueue-list-queues"></a>

以下代码片段显示了 SQS 客户端区域中现有队列的列表。

[本主题末尾](#DeleteSqsQueue-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to show a list of the existing queues
    private static async Task ListQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine("\nList of queues:");
      foreach(var qUrl in responseList.QueueUrls)
        Console.WriteLine($"- {qUrl}");
    }
```

## 完整代码
<a name="DeleteSqsQueue-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c25c21c25b5b1"></a>

NuGet 包裹：
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

编程元素：
+ 命名空间 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQS.html)

  [Amazon 上](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html)课 SQSClient
+ 命名空间 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQSModel.html)

  班级 [ListQueuesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TListQueuesResponse.html)

### 代码
<a name="w2aac19c15c25c21c25b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSDeleteQueue
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to update a queue
  class Program
  {
    private const int TimeToWait = 60;

    static async Task Main(string[] args)
    {
      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // If no command-line arguments, just show a list of the queues
      if(args.Length == 0)
      {
        Console.WriteLine("\nUsage: SQSCreateQueue queue_url");
        Console.WriteLine("   queue_url - The URL of the queue you want to delete.");
        Console.WriteLine("\nNo arguments specified.");
        Console.Write("Do you want to see a list of the existing queues? ((y) or n): ");
        var response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await ListQueues(sqsClient);
        return;
      }

      // If given a queue URL, delete that queue
      if(args[0].StartsWith("https://sqs."))
      {
        // Delete the queue
        await DeleteQueue(sqsClient, args[0]);
        // Wait for a little while because it takes a while for the queue to disappear
        await Wait(sqsClient, TimeToWait, args[0]);
        // Show a list of the remaining queues
        await ListQueues(sqsClient);
      }
      else
      {
        Console.WriteLine("The command-line argument isn't a queue URL:");
        Console.WriteLine($"{args[0]}");
      }
    }


    //
    // Method to delete an SQS queue
    private static async Task DeleteQueue(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"Deleting queue {qUrl}...");
      await sqsClient.DeleteQueueAsync(qUrl);
      Console.WriteLine($"Queue {qUrl} has been deleted.");
    }


    //
    // Method to wait up to a given number of seconds
    private static async Task Wait(
      IAmazonSQS sqsClient, int numSeconds, string qUrl)
    {
      Console.WriteLine($"Waiting for up to {numSeconds} seconds.");
      Console.WriteLine("Press any key to stop waiting. (Response might be slightly delayed.)");
      for(int i=0; i<numSeconds; i++)
      {
        Console.Write(".");
        Thread.Sleep(1000);
        if(Console.KeyAvailable) break;

        // Check to see if the queue is gone yet
        var found = false;
        ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
        foreach(var url in responseList.QueueUrls)
        {
          if(url == qUrl)
          {
            found = true;
            break;
          }
        }
        if(!found) break;
      }
    }


    //
    // Method to show a list of the existing queues
    private static async Task ListQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine("\nList of queues:");
      foreach(var qUrl in responseList.QueueUrls)
        Console.WriteLine($"- {qUrl}");
    }
  }
}
```

## 其他注意事项
<a name="DeleteSqsQueue-additional"></a>
+ `DeleteQueueAsync` API 调用不会检查您要删除的队列是否被用作死信队列。可以用更复杂的程序来检查这一点。
+ 您还可以在 [Amazon SQS 控制台](https://console.aws.amazon.com/sqs)中查看队列列表和此示例的结果。

# 发送 Amazon SMS 消息
<a name="SendMessage"></a>

[此示例向您展示如何使用向 Amazon SQS 队列发送消息，您可以通过[编程方式](CreateQueue.md)或使用 Amazon SQS 控制台创建该队列。 适用于 .NET 的 AWS SDK](https://console.aws.amazon.com/sqs)应用程序向队列发送一条消息，然后发送一批消息。然后，应用程序等待用户输入，这些输入可以是要发送到队列的额外消息或退出应用程序的请求。

此示例和[下一个有关接收消息的示例](ReceiveMessage.md)可以一起使用，以查看 Amazon SQS 中的消息流。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#SendMessage-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [发送消息](#SendMessage-send-message)
+ [发送一批消息](#SendMessage-send-batch)
+ [从队列中删除所有消息](#SendMessage-purge-messages)
+ [完整代码](#SendMessage-complete-code)
+ [其他注意事项](#SendMessage-additional)

## 发送消息
<a name="SendMessage-send-message"></a>

以下代码片段将消息发送到由给定队列 URL 标识的队列。

[本主题末尾](#SendMessage-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to put a message on a queue
    // Could be expanded to include message attributes, etc., in a SendMessageRequest
    private static async Task SendMessage(
      IAmazonSQS sqsClient, string qUrl, string messageBody)
    {
      SendMessageResponse responseSendMsg =
        await sqsClient.SendMessageAsync(qUrl, messageBody);
      Console.WriteLine($"Message added to queue\n  {qUrl}");
      Console.WriteLine($"HttpStatusCode: {responseSendMsg.HttpStatusCode}");
    }
```

## 发送一批消息
<a name="SendMessage-send-batch"></a>

以下代码片段向由给定队列 URL 标识的队列发送一批消息。

[本主题末尾](#SendMessage-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to put a batch of messages on a queue
    // Could be expanded to include message attributes, etc.,
    // in the SendMessageBatchRequestEntry objects
    private static async Task SendMessageBatch(
      IAmazonSQS sqsClient, string qUrl, List<SendMessageBatchRequestEntry> messages)
    {
      Console.WriteLine($"\nSending a batch of messages to queue\n  {qUrl}");
      SendMessageBatchResponse responseSendBatch =
        await sqsClient.SendMessageBatchAsync(qUrl, messages);
      // Could test responseSendBatch.Failed here
      foreach(SendMessageBatchResultEntry entry in responseSendBatch.Successful)
        Console.WriteLine($"Message {entry.Id} successfully queued.");
    }
```

## 从队列中删除所有消息
<a name="SendMessage-purge-messages"></a>

以下代码片段删除了来自由给定队列 URL 标识的队列的所有消息。这也称为*清除队列*。

[本主题末尾](#SendMessage-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to delete all messages from the queue
    private static async Task DeleteAllMessages(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"\nPurging messages from queue\n  {qUrl}...");
      PurgeQueueResponse responsePurge = await sqsClient.PurgeQueueAsync(qUrl);
      Console.WriteLine($"HttpStatusCode: {responsePurge.HttpStatusCode}");
    }
```

## 完整代码
<a name="SendMessage-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c25c23c25b5b1"></a>

NuGet 包裹：
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

编程元素：
+ 命名空间 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQS.html)

  [Amazon 上](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html)课 SQSClient
+ 命名空间 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQSModel.html)

  班级 [PurgeQueueResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TPurgeQueueResponse.html)

  班级 [SendMessageBatchResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSendMessageBatchResponse.html)

  班级 [SendMessageResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSendMessageResponse.html)

  班级 [SendMessageBatchRequestEntry](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSendMessageBatchRequestEntry.html)

  班级 [SendMessageBatchResultEntry](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSendMessageBatchResultEntry.html)

### 代码
<a name="w2aac19c15c25c23c25b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSSendMessages
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to send messages to a queue
  class Program
  {
    // Some example messages to send to the queue
    private const string JsonMessage = "{\"product\":[{\"name\":\"Product A\",\"price\": \"32\"},{\"name\": \"Product B\",\"price\": \"27\"}]}";
    private const string XmlMessage = "<products><product name=\"Product A\" price=\"32\" /><product name=\"Product B\" price=\"27\" /></products>";
    private const string CustomMessage = "||product|Product A|32||product|Product B|27||";
    private const string TextMessage = "Just a plain text message.";

    static async Task Main(string[] args)
    {
      // Do some checks on the command-line
      if(args.Length == 0)
      {
        Console.WriteLine("\nUsage: SQSSendMessages queue_url");
        Console.WriteLine("   queue_url - The URL of an existing SQS queue.");
        return;
      }
      if(!args[0].StartsWith("https://sqs."))
      {
        Console.WriteLine("\nThe command-line argument isn't a queue URL:");
        Console.WriteLine($"{args[0]}");
        return;
      }

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // (could verify that the queue exists)
      // Send some example messages to the given queue
      // A single message
      await SendMessage(sqsClient, args[0], JsonMessage);

      // A batch of messages
      var batchMessages = new List<SendMessageBatchRequestEntry>{
        new SendMessageBatchRequestEntry("xmlMsg", XmlMessage),
        new SendMessageBatchRequestEntry("customeMsg", CustomMessage),
        new SendMessageBatchRequestEntry("textMsg", TextMessage)};
      await SendMessageBatch(sqsClient, args[0], batchMessages);

      // Let the user send their own messages or quit
      await InteractWithUser(sqsClient, args[0]);

      // Delete all messages that are still in the queue
      await DeleteAllMessages(sqsClient, args[0]);
    }


    //
    // Method to put a message on a queue
    // Could be expanded to include message attributes, etc., in a SendMessageRequest
    private static async Task SendMessage(
      IAmazonSQS sqsClient, string qUrl, string messageBody)
    {
      SendMessageResponse responseSendMsg =
        await sqsClient.SendMessageAsync(qUrl, messageBody);
      Console.WriteLine($"Message added to queue\n  {qUrl}");
      Console.WriteLine($"HttpStatusCode: {responseSendMsg.HttpStatusCode}");
    }


    //
    // Method to put a batch of messages on a queue
    // Could be expanded to include message attributes, etc.,
    // in the SendMessageBatchRequestEntry objects
    private static async Task SendMessageBatch(
      IAmazonSQS sqsClient, string qUrl, List<SendMessageBatchRequestEntry> messages)
    {
      Console.WriteLine($"\nSending a batch of messages to queue\n  {qUrl}");
      SendMessageBatchResponse responseSendBatch =
        await sqsClient.SendMessageBatchAsync(qUrl, messages);
      // Could test responseSendBatch.Failed here
      foreach(SendMessageBatchResultEntry entry in responseSendBatch.Successful)
        Console.WriteLine($"Message {entry.Id} successfully queued.");
    }


    //
    // Method to get input from the user
    // They can provide messages to put in the queue or exit the application
    private static async Task InteractWithUser(IAmazonSQS sqsClient, string qUrl)
    {
      string response;
      while (true)
      {
        // Get the user's input
        Console.WriteLine("\nType a message for the queue or \"exit\" to quit:");
        response = Console.ReadLine();
        if(response.ToLower() == "exit") break;

        // Put the user's message in the queue
        await SendMessage(sqsClient, qUrl, response);
      }
    }


    //
    // Method to delete all messages from the queue
    private static async Task DeleteAllMessages(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"\nPurging messages from queue\n  {qUrl}...");
      PurgeQueueResponse responsePurge = await sqsClient.PurgeQueueAsync(qUrl);
      Console.WriteLine($"HttpStatusCode: {responsePurge.HttpStatusCode}");
    }
  }
}
```

## 其他注意事项
<a name="SendMessage-additional"></a>
+ 有关消息的各种限制（包括允许的字符）的信息，请参阅 [Amazon Simple Queue Service 开发人员指南](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/)中[与消息相关的配额](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-quotas.html#quotas-messages)部分。
+ 消息会一直保留在队列中，直到被删除或队列被清除。当应用程序收到一条消息时，即使它仍然存在于队列中，它也不会出现在队列中。有关可见性超时的更多信息，请参阅 [Amazon SQS 可见性超时](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html)。
+ 除了消息正文外，您还可以为消息添加属性。有关更多信息，请参阅[消息元数据](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html)。

# 接收 Amazon SQS 消息
<a name="ReceiveMessage"></a>

[此示例向您展示如何使用接收 适用于 .NET 的 AWS SDK 来自 Amazon SQS 队列的消息，您可以通过[编程方式](CreateQueue.md)或使用 Amazon SQS 控制台创建该队列。](https://console.aws.amazon.com/sqs)应用程序从队列中读取一条消息，处理该消息（在本例中，在控制台上显示消息正文），然后从队列中删除该消息。应用程序会重复这些步骤，直到用户在键盘上键入一个键。

此示例和[前面有关接收消息的示例](SendMessage.md)可以一起使用，以查看 Amazon SQS 中的消息流。

以下各节提供了此示例的片段。此后显示了[该示例的完整代码](#ReceiveMessage-complete-code)，并且可以按原样构建和运行。

**Topics**
+ [接收消息](#ReceiveMessage-receive)
+ [删除消息](#ReceiveMessage-delete)
+ [完整代码](#ReceiveMessage-complete-code)
+ [其他注意事项](#ReceiveMessage-additional)

## 接收消息
<a name="ReceiveMessage-receive"></a>

以下代码片段从由给定队列 URL 标识的队列接收消息。

[本主题末尾](#ReceiveMessage-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to read a message from the given queue
    // In this example, it gets one message at a time
    private static async Task<ReceiveMessageResponse> GetMessage(
      IAmazonSQS sqsClient, string qUrl, int waitTime=0)
    {
      return await sqsClient.ReceiveMessageAsync(new ReceiveMessageRequest{
        QueueUrl=qUrl,
        MaxNumberOfMessages=MaxMessages,
        WaitTimeSeconds=waitTime
        // (Could also request attributes, set visibility timeout, etc.)
      });
    }
```

## 删除消息
<a name="ReceiveMessage-delete"></a>

以下代码片段删除了来自由给定队列 URL 标识的队列的消息。

[本主题末尾](#ReceiveMessage-complete-code)的示例显示了此片段的使用情况。

```
    //
    // Method to delete a message from a queue
    private static async Task DeleteMessage(
      IAmazonSQS sqsClient, Message message, string qUrl)
    {
      Console.WriteLine($"\nDeleting message {message.MessageId} from queue...");
      await sqsClient.DeleteMessageAsync(qUrl, message.ReceiptHandle);
    }
```

## 完整代码
<a name="ReceiveMessage-complete-code"></a>

本部分显示了本示例的相关参考和完整代码。

### SDK 参考
<a name="w2aac19c15c25c25c21b5b1"></a>

NuGet 包裹：
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

编程元素：
+ 命名空间 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQS.html)

  [Amazon 上](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html)课 SQSClient
+ 命名空间 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/NSQSModel.html)

  班级 [ReceiveMessageRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TReceiveMessageRequest.html)

  班级 [ReceiveMessageResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TReceiveMessageResponse.html)

### 代码
<a name="w2aac19c15c25c25c21b7b1"></a>

```
using System;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSReceiveMessages
{
  class Program
  {
    private const int MaxMessages = 1;
    private const int WaitTime = 2;
    static async Task Main(string[] args)
    {
      // Do some checks on the command-line
      if(args.Length == 0)
      {
        Console.WriteLine("\nUsage: SQSReceiveMessages queue_url");
        Console.WriteLine("   queue_url - The URL of an existing SQS queue.");
        return;
      }
      if(!args[0].StartsWith("https://sqs."))
      {
        Console.WriteLine("\nThe command-line argument isn't a queue URL:");
        Console.WriteLine($"{args[0]}");
        return;
      }

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // (could verify that the queue exists)
      // Read messages from the queue and perform appropriate actions
      Console.WriteLine($"Reading messages from queue\n  {args[0]}");
      Console.WriteLine("Press any key to stop. (Response might be slightly delayed.)");
      do
      {
        var msg = await GetMessage(sqsClient, args[0], WaitTime);
        if(msg.Messages.Count != 0)
        {
          if(ProcessMessage(msg.Messages[0]))
            await DeleteMessage(sqsClient, msg.Messages[0], args[0]);
        }
      } while(!Console.KeyAvailable);
    }


    //
    // Method to read a message from the given queue
    // In this example, it gets one message at a time
    private static async Task<ReceiveMessageResponse> GetMessage(
      IAmazonSQS sqsClient, string qUrl, int waitTime=0)
    {
      return await sqsClient.ReceiveMessageAsync(new ReceiveMessageRequest{
        QueueUrl=qUrl,
        MaxNumberOfMessages=MaxMessages,
        WaitTimeSeconds=waitTime
        // (Could also request attributes, set visibility timeout, etc.)
      });
    }


    //
    // Method to process a message
    // In this example, it simply prints the message
    private static bool ProcessMessage(Message message)
    {
      Console.WriteLine($"\nMessage body of {message.MessageId}:");
      Console.WriteLine($"{message.Body}");
      return true;
    }


    //
    // Method to delete a message from a queue
    private static async Task DeleteMessage(
      IAmazonSQS sqsClient, Message message, string qUrl)
    {
      Console.WriteLine($"\nDeleting message {message.MessageId} from queue...");
      await sqsClient.DeleteMessageAsync(qUrl, message.ReceiptHandle);
    }
  }
}
```

## 其他注意事项
<a name="ReceiveMessage-additional"></a>
+ 为了指定长轮询，此示例在每次调用 `ReceiveMessageAsync` 方法时都使用该 `WaitTimeSeconds` 属性。

  您还可以在[创建](CreateQueue.md)或[更新](UpdateSqsQueue.md)队列时使用 `ReceiveMessageWaitTimeSeconds` 属性为队列中的所有消息指定长轮询。

  有关短轮询与长轮询的信息，请参阅《Amazon Simple Queue Service 开发人员指南》**中的[短轮询与长轮询](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html)。
+ 在消息处理过程中，您可以使用接收句柄来更改消息可见性超时。有关如何执行此操作的信息，请参阅 [Amazon SQSClient](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SQS/TSQSClient.html) 类`ChangeMessageVisibilityAsync`的方法。
+ 调用 `DeleteMessageAsync` 方法将无条件地从队列中删除消息，而无论可见性超时设置如何。

# 将 AWS Lambda 用于计算服务
<a name="aws-lambda"></a>

适用于 .NET 的 AWS SDK 支持 AWS Lambda，因此您可以运行代码，而无需预置或管理服务器。有关更多信息，请参阅 [AWS Lambda 产品页面](https://aws.amazon.com/lambda/)和 [AWS Lambda 开发人员指南](https://docs.aws.amazon.com/lambda/latest/dg/)，尤其是[使用 C\$1](https://docs.aws.amazon.com/lambda/latest/dg/lambda-csharp.html) 部分。

## API
<a name="w2aac19c17b5"></a>

适用于 .NET 的 AWS SDK为 AWS Lambda 提供了 API。这些 API 使您能够使用[函数](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-function)、[触发器](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-trigger)和[事件](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-event)等 Lambda 功能。要查看完整的 API 集，请参阅[适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)中的 [Lambda](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Lambda/NLambda.html)。

Lambda API 由 [NuGet 程序包提供](https://www.nuget.org/packages?page=2&q=aws%20lambda&sortBy=relevance)。

## 先决条件
<a name="w2aac19c17b7"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

## 其他信息
<a name="w2aac19c17b9"></a>

有关通过 .NET Aspire 使用 AWS Lambda 进行开发的信息，请参阅[将 AWS 与 .NET Aspire 集成](aspire-integrations.md)。

## 主题
<a name="w2aac19c17c11"></a>

**Topics**
+ [API](#w2aac19c17b5)
+ [先决条件](#w2aac19c17b7)
+ [其他信息](#w2aac19c17b9)
+ [主题](#w2aac19c17c11)
+ [Lambda 注释](aws-lambda-annotations.md)

# 使用注解来编写 AWS Lambda 函数
<a name="aws-lambda-annotations"></a>

在编写 Lambda 函数时，您有时需要编写大量的处理程序代码，并更新 AWS CloudFormation 模板以及执行其它任务。Lambda 注释是一个框架，有助于减轻 .NET 6 Lambda 函数的负担，从而让使用 C\$1 编写 Lambda 的体验更加自然。

如果要举例说明使用 Lambda 注释框架的好处，请考虑以下将两个数字相加的代码片段。

**不使用 Lambda 注释**

```
public class Functions
{
    public APIGatewayProxyResponse LambdaMathPlus(APIGatewayProxyRequest request, ILambdaContext context)
    {
        if (!request.PathParameters.TryGetValue("x", out var xs))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }
        if (!request.PathParameters.TryGetValue("y", out var ys))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }

        var x = int.Parse(xs);
        var y = int.Parse(ys);

        return new APIGatewayProxyResponse
        {
            StatusCode = (int)HttpStatusCode.OK,
            Body = (x + y).ToString(),
            Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
        };
    } 
}
```

**使用 Lambda 注释**

```
public class Functions
{
    [LambdaFunction]
    [RestApi("/plus/{x}/{y}")]
    public int Plus(int x, int y)
    {
        return x + y;
    }
}
```

如示例所示，Lambda 注释可以消除对某些 Boilerplate 代码的需求。

有关如何使用该框架以及更多信息，请参阅以下资源：
+ [GitHub 自述](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md)文件，其中包含有关 Lambda 注释 API 和属性的文档。
+ 有关 Lambda 注释的[博客文章](https://aws.amazon.com/blogs/developer/net-lambda-annotations-framework/)。
+ [https://www.nuget.org/packages/Amazon.Lambda.Annotations](https://www.nuget.org/packages/Amazon.Lambda.Annotations) NuGet 程序包。
+ GitHub 上的[照片资产管理项目](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/PhotoAssetManager)。具体而言，请参阅项目[自述文件](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/dotnetv3/cross-service/PhotoAssetManager/README.md)中的 [PamApiAnnotations](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/PhotoAssetManager/PamApiAnnotations) 文件夹和对 Lambda 注释的引用。

# 适用于 .NET 的 AWS SDK 的高级库和框架
<a name="high-level-libraries"></a>

以下各节包含有关不属于 SDK 核心功能的高级库和框架的信息。这些库和框架使用核心 SDK 功能来创建可简化某些任务的功能。

如果您不熟悉适用于 .NET 的 AWS SDK，则可能需要先查看[快速了解](quick-start.md)主题。其中提供了对软件开发工具包的简单介绍。

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

**Topics**
+ [Message Processing Framework](msg-proc-fw.md)
+ [将 AWS 与 .NET Aspire 集成](aspire-integrations.md)

# AWS Message Processing Framework for .NET
<a name="msg-proc-fw"></a>

AWS Message Processing Framework for .NET 是一个 AWS 原生框架，它简化了使用 Amazon Simple Queue Service (SQS)、Amazon Simple Notification Service (SNS)、Amazon EventBridge 等 AWS 服务的 .NET 消息处理应用程序的开发。该框架减少了开发人员需要编写的样板代码量，使您能够在发布和使用消息时专注于业务逻辑。有关该框架如何简化开发的详细信息，请参阅博客文章 [Introducing the AWS Message Processing Framework for .NET (Preview)](https://aws.amazon.com/blogs/developer/introducing-the-aws-message-processing-framework-for-net-preview/)。第一部分特别提供了演示，展示了使用低级别 API 调用和使用框架之间的区别。

Message Processing Framework 支持以下活动和功能：
+ 向 SQS 发送消息，向 SNS 和 EventBridge 发布事件。
+ 使用长时间运行的轮询器接收和处理来自 SQS 的消息，这种方式通常用于后台服务。这包括在处理消息时管理可见性超时，以防止其他客户端处理该消息。
+ 在 AWS Lambda 函数中处理消息。
+ FIFO（先进先出）SQS 队列和 SNS 主题。
+ 用于记录的 OpenTelemetry。

有关这些活动和功能的详细信息，请参阅[博客文章](https://aws.amazon.com/blogs/developer/introducing-the-aws-message-processing-framework-for-net-preview/)的 **功能** 部分以及下列主题。

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

**其他资源**
+ [NuGet.org](https://www.nuget.org/) 上的 [https://www.nuget.org/packages/AWS.Messaging/](https://www.nuget.org/packages/AWS.Messaging/) 软件包。
+ [API 参考](https://aws.github.io/aws-dotnet-messaging/)。
+ GitHub 存储库中的`README`文件，网址为 [https://github.com/aws/aws-dotnet-messaging/](https://github.com/aws/aws-dotnet-messaging/)
+ Microsoft 的 [.NET 依赖项注入](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection)。
+ Microsoft 的 [.NET 通用主机](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host)。

**Topics**
+ [开始使用](msg-proc-fw-get-started.md)
+ [发布消息](msg-proc-fw-publish.md)
+ [使用消息](msg-proc-fw-consume.md)
+ [FIFO](msg-proc-fw-fifo.md)
+ [日志记录和 OpenTelemetry](msg-proc-fw-telemetry.md)
+ [自定义](msg-proc-fw-customize.md)
+ [安全性](msg-proc-fw-security.md)

# 开始使用 AWS Message Processing Framework for .NET
<a name="msg-proc-fw-get-started"></a>

开始之前，请确保您已[完成环境和项目的设置](net-dg-config.md)。还要查看[软件开发工具包功能](net-dg-sdk-features.md)中的信息。

本主题介绍如何开始使用 Message Processing Framework。除了先决条件和配置信息外，本主题还提供相关教程来展示如何实现常见场景。

## 先决条件和配置
<a name="mpf-get-started-prereq"></a>
+ 您为应用程序提供的凭证必须具备消息服务及其所用操作的相应权限。有关更多信息，请参阅 [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-authentication-and-access-control.html)、[SNS](https://docs.aws.amazon.com/sns/latest/dg/security-iam.html) 和 [EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-iam.html) 各自开发人员指南中的安全性主题。另请参阅 GitHub 上[自述文件](https://github.com/aws/aws-dotnet-messaging/)中关于特定[权限](https://github.com/aws/aws-dotnet-messaging/blob/main/README.md#permissions)的部分。
+ 要使用 AWS Message Processing Framework for .NET，您必须将 [https://www.nuget.org/packages/AWS.Messaging](https://www.nuget.org/packages/AWS.Messaging) NuGet 软件包添加到您的项目中。例如：

  ```
  dotnet add package AWS.Messaging
  ```
+ 该框架与 .NET 的[依赖项注入（DI）服务容器](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection)集成。您可以在应用程序启动过程中通过调用 `AddAWSMessageBus` 将其添加到 DI 容器来配置框架。

  ```
  var builder = WebApplication.CreateBuilder(args);
  
  // Register the AWS Message Processing Framework for .NET
  builder.Services.AddAWSMessageBus(builder =>
  {
      // Register that you'll publish messages of type ChatMessage to an existing queue
      builder.AddSQSPublisher<ChatMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");
  });
  ```

## 教程
<a name="mpf-get-started-tutorial"></a>

本教程演示了如何使用 AWS Message Processing Framework for .NET。它创建了两个应用程序：一个 ASP.NET Core Minimal API，用于在 API 端点收到请求时向 Amazon SQS 队列发送消息；一个长时间运行的控制台应用程序，用于轮询和处理这些消息。
+ 本教程中的说明偏好使用 .NET CLI，但您既可以使用 .NET CLI 等跨平台工具，也可以使用 Microsoft Visual Studio 来完成本教程。有关这些工具的信息，请参阅[安装和配置工具链](net-dg-dev-env.md)。
+ 本教程假定您使用 `[default]` 配置文件作为凭证。它还假定短期凭证具有发送和接收 Amazon SQS 消息的相应权限。有关更多信息，请参阅[使用配置 SDK 身份验证 AWS](creds-idc.md)和 [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-authentication-and-access-control.html) 的安全性主题。

**注意**  
运行本教程可能会产生 SQS 消息收发费用。

### 步骤
<a name="mpf-tutorial-steps"></a>
+ [创建 SQS 队列](#mpf-tutorial-queue)
+ [创建并运行发布应用程序](#mpf-tutorial-publish)
+ [创建并运行处理应用程序](#mpf-tutorial-handle)
+ [清理](#mpf-tutorial-cleanup)

### 创建 SQS 队列
<a name="mpf-tutorial-queue"></a>

本教程需要一个 SQS 队列来向其发送消息和从中接收消息。可以使用 AWS CLI 或 AWS Tools for PowerShell 的以下命令之一来创建队列。记下返回的队列 URL，以便可以在后续框架配置过程中指定它。

------
#### [ AWS CLI ]

```
aws sqs create-queue --queue-name DemoQueue
```

------
#### [ AWS Tools for PowerShell ]

```
New-SQSQueue -QueueName DemoQueue
```

------

### 创建并运行发布应用程序
<a name="mpf-tutorial-publish"></a>

按照以下程序创建并运行发布应用程序。

1. 打开命令提示符或终端。查找或创建可以在其中创建 .NET 项目的操作系统文件夹。

1. 在该文件夹中，运行以下命令以创建 .NET 项目。

   ```
   dotnet new webapi --name Publisher
   ```

1. 导航到新项目的文件夹。在 AWS Message Processing Framework for .NET 上添加依赖项。

   ```
   cd Publisher
   dotnet add package AWS.Messaging
   ```
**注意**  
如果您使用 AWS IAM Identity Center 进行身份验证，请务必同时添加 `AWSSDK.SSO` 和 `AWSSDK.SSOOIDC`。

1. 使用以下代码替换 `Program.cs` 中的代码。

   ```
   using AWS.Messaging;
   using Microsoft.AspNetCore.Mvc;
   using Publisher;
   
   var builder = WebApplication.CreateBuilder(args);
   
   // Add services to the container.
   // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle.
   builder.Services.AddEndpointsApiExplorer();
   builder.Services.AddSwaggerGen();
   
   
   // Configure the AWS Message Processing Framework for .NET.
   builder.Services.AddAWSMessageBus(builder =>
   {
       // Check for input SQS URL.
       // The SQS URL should be passed as a command line argument or set in the Debug launch profile.
       if ((args.Length == 1) && (args[0].Contains("https://sqs.")))
       {
           // Register that you'll publish messages of type GreetingMessage:
           // 1. To a specified queue.
           // 2. Using the message identifier "greetingMessage", which will be used
           //    by handlers to route the message to the appropriate handler.
           builder.AddSQSPublisher<GreetingMessage>(args[0], "greetingMessage");
       }
       // You can map additional message types to queues or topics here as well.
   });
   var app = builder.Build();
   
   
   // Configure the HTTP request pipeline.
   if (app.Environment.IsDevelopment())
   {
       app.UseSwagger();
       app.UseSwaggerUI();
   }
   
   app.UseHttpsRedirection();
   
   // Create an API Endpoint that receives GreetingMessage objects
   // from the caller and then sends them as an SQS message.
   app.MapPost("/greeting", async ([FromServices] IMessagePublisher publisher, Publisher.GreetingMessage message) =>
       {
           return await PostGreeting(message, publisher);
       })
   .WithName("SendGreeting")
   .WithOpenApi();
   
   app.Run();
   
   public partial class Program
   {
       /// <summary>
       /// Endpoint for posting a greeting message.
       /// </summary>
       /// <param name="greetingMessage">The greeting message.</param>
       /// <param name="messagePublisher">The message publisher.</param>
       /// <returns>Async task result.</returns>
       public static async Task<IResult> PostGreeting(GreetingMessage greetingMessage,
           IMessagePublisher messagePublisher)
       {
           if (greetingMessage.SenderName == null || greetingMessage.Greeting == null)
           {
               return Results.BadRequest();
           }
   
           // Publish the message to the queue configured above.
           await messagePublisher.PublishAsync(greetingMessage);
   
           return Results.Ok();
       }
   }
   
   namespace Publisher
   {
       /// <summary>
       /// This class represents the message contents.
       /// </summary>
       public class GreetingMessage
       {
           public string? SenderName { get; set; }
           public string? Greeting { get; set; }
       }
   }
   ```

1. 运行以下命令。这应该会打开一个带有 Swagger UI 的浏览器窗口，让您能够浏览和测试 API。

   ```
   dotnet watch run <queue URL created earlier>
   ```

1. 打开 `/greeting` 端点并选择**试用**。

1. 为消息指定 `senderName` 和 `greeting` 值，然后选择**执行**。这会调用您的 API，该 API 会发送 SQS 消息。

### 创建并运行处理应用程序
<a name="mpf-tutorial-handle"></a>

按照以下程序创建并运行处理应用程序。

1. 打开命令提示符或终端。查找或创建可以在其中创建 .NET 项目的操作系统文件夹。

1. 在该文件夹中，运行以下命令以创建 .NET 项目。

   ```
   dotnet new console --name Handler
   ```

1. 导航到新项目的文件夹。在 AWS Message Processing Framework for .NET 上添加依赖项。还要添加 `Microsoft.Extensions.Hosting` 软件包，支持您通过 [.NET 通用主机](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host)配置框架。

   ```
   cd Handler
   dotnet add package AWS.Messaging
   dotnet add package Microsoft.Extensions.Hosting
   ```
**注意**  
如果您使用 AWS IAM Identity Center 进行身份验证，请务必同时添加 `AWSSDK.SSO` 和 `AWSSDK.SSOOIDC`。

1. 使用以下代码替换 `Program.cs` 中的代码。

   ```
   using AWS.Messaging;
   using Handler;
   using Microsoft.Extensions.DependencyInjection;
   using Microsoft.Extensions.Hosting;
   
   var builder = Host.CreateDefaultBuilder(args);
   
   builder.ConfigureServices(services =>
   {
       // Register the AWS Message Processing Framework for .NET.
       services.AddAWSMessageBus(builder =>
       {
           // Check for input SQS URL.
           // The SQS URL should be passed as a command line argument or set in the Debug launch profile.
           if ((args.Length == 1) && (args[0].Contains("https://sqs.")))
           {
               // Register you'll poll the following queue.
               builder.AddSQSPoller(args[0]);
   
               // And that messages of type "greetingMessage" should be:
               // 1. Deserialized as GreetingMessage objects.
               // 2. Which are then passed to GreetingMessageHandler.
               builder.AddMessageHandler<GreetingMessageHandler, GreetingMessage>("greetingMessage");
   
           }
           // You can add additional message handlers here, using different message types. 
       });
   });
   
   var host = builder.Build();
   await host.RunAsync();
   
   namespace Handler
   {
       /// <summary>
       /// This class represents the message contents.
       /// </summary>
       public class GreetingMessage
       {
           public string? SenderName { get; set; }
           public string? Greeting { get; set; }
       }
   
       /// <summary>
       /// This handler is invoked each time you receive the message.
       /// </summary>
       public class GreetingMessageHandler : IMessageHandler<GreetingMessage>
       {
           public Task<MessageProcessStatus> HandleAsync(
               MessageEnvelope<GreetingMessage> messageEnvelope,
               CancellationToken token = default)
           {
               Console.WriteLine(
                   $"Received message {messageEnvelope.Message.Greeting} from {messageEnvelope.Message.SenderName}");
               return Task.FromResult(MessageProcessStatus.Success());
           }
       }
   }
   ```

1. 运行以下命令。这会启动一个长时间运行的轮询器。

   ```
   dotnet run <queue URL created earlier>
   ```

   启动后不久，应用程序将收到本教程第一部分中发送的消息并记录以下消息：

   ```
   Received message {greeting} from {senderName}
   ```

1. 按 `Ctrl+C` 停止轮询器。

### 清理
<a name="mpf-tutorial-cleanup"></a>

使用 AWS CLI 或 AWS Tools for PowerShell 的以下命令之一删除队列。

------
#### [ AWS CLI ]

```
aws sqs delete-queue --queue-url "<queue URL created earlier>"
```

------
#### [ AWS Tools for PowerShell ]

```
Remove-SQSQueue -QueueUrl "<queue URL created earlier>"
```

------

# 使用 AWS Message Processing Framework for .NET 发布消息
<a name="msg-proc-fw-publish"></a>

AWS Message Processing Framework for .NET 支持发布一种或多种消息类型、处理一种或多种消息类型，或者在同一个应用程序中同时执行这两种操作。

以下代码显示了向不同 AWS 服务发布不同消息类型的应用程序的配置。

```
var builder = WebApplication.CreateBuilder(args);

// Register the AWS Message Processing Framework for .NET
builder.Services.AddAWSMessageBus(builder =>
{
    // Register that you'll send messages of type ChatMessage to an existing queue
    builder.AddSQSPublisher<ChatMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");

    // Register that you'll publish messages of type OrderInfo to an existing SNS topic
    builder.AddSNSPublisher<OrderInfo>("arn:aws:sns:us-west-2:012345678910:MyAppProd");

    // Register that you'll publish messages of type FoodItem to an existing EventBridge bus
    builder.AddEventBridgePublisher<FoodItem>("arn:aws:events:us-west-2:012345678910:event-bus/default");
});
```

在启动过程中注册框架后，将通用 `IMessagePublisher` 注入到代码中。调用其 `PublishAsync` 方法来发布上面配置的任何消息类型。通用发布者将根据消息类型确定要将消息路由到的目的地。

 在以下示例中，ASP.NET MVC 控制器接收来自用户的 `ChatMessage` 消息和 `OrderInfo` 事件，然后分别将其发布到 Amazon SQS 和 Amazon SNS。两种消息类型都可以使用上面配置的通用发布者发布。

```
[ApiController]
[Route("[controller]")]
public class PublisherController : ControllerBase
{
    private readonly IMessagePublisher _messagePublisher;

    public PublisherController(IMessagePublisher messagePublisher)
    {
        _messagePublisher = messagePublisher;
    }

    [HttpPost("chatmessage", Name = "Chat Message")]
    public async Task<IActionResult> PublishChatMessage([FromBody] ChatMessage message)
    {
        // Perform business and validation logic on the ChatMessage here.
        if (message == null)
        {
            return BadRequest("A chat message was not submitted. Unable to forward to the message queue.");
        }
        if (string.IsNullOrEmpty(message.MessageDescription))
        {
            return BadRequest("The MessageDescription cannot be null or empty.");
        }

        // Send the ChatMessage to SQS, using the generic publisher.
        await _messagePublisher.PublishAsync(message);

        return Ok();
    }

    [HttpPost("order", Name = "Order")]
    public async Task<IActionResult> PublishOrder([FromBody] OrderInfo message)
    {
        if (message == null)
        {
            return BadRequest("An order was not submitted.");
        }

        // Publish the OrderInfo to SNS, using the generic publisher.
        await _messagePublisher.PublishAsync(message);

        return Ok();
    }
}
```

为了将消息路由到相应的处理逻辑，框架使用称为*消息类型标识符*的元数据。默认情况下，这是消息的 .NET 类型的完整名称，包括其程序集名称。如果既要发送消息，又要处理消息，那么当跨项目共享消息对象的定义时，这种机制能很好地发挥作用。但是，如果在不同的命名空间中重新定义消息，或者您要与其他框架或编程语言交换消息，则可能需要覆盖消息类型标识符。

```
var builder = Host.CreateDefaultBuilder(args);

builder.ConfigureServices(services =>
{
    // Register the AWS Message Processing Framework for .NET
    services.AddAWSMessageBus(builder =>
    {
        // Register that you'll publish messages of type GreetingMessage to an existing queue
        builder.AddSQSPublisher<GreetingMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", "greetingMessage");
    });
});
```

## 特定于服务的发布者
<a name="service-specific-publishers"></a>

上面显示的示例使用通用 `IMessagePublisher`，它可以根据配置的消息类型发布到任何支持的 AWS 服务。该框架还为 Amazon SQS、Amazon SNS 和 Amazon EventBridge 提供特定于服务的发布者。这些特定的发布者会显示仅适用于特定服务的选项，并且这些选项可以使用类型 `ISQSPublisher`、`ISNSPublisher` 和 `IEventBridgePublisher` 完成注入。

例如，向 SQS FIFO 队列发送消息时，您必须设置相应的[消息组 ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-key-terms.html)。以下代码显示的还是 `ChatMessage` 示例，但这次使用 `ISQSPublisher` 来设置特定于 SQS 的选项。

```
public class PublisherController : ControllerBase
{
    private readonly ISQSPublisher _sqsPublisher;

    public PublisherController(ISQSPublisher sqsPublisher)
    {
        _sqsPublisher = sqsPublisher;
    }

    [HttpPost("chatmessage", Name = "Chat Message")]
    public async Task<IActionResult> PublishChatMessage([FromBody] ChatMessage message)
    {
        // Perform business and validation logic on the ChatMessage here
        if (message == null)
        {
            return BadRequest("A chat message was not submitted. Unable to forward to the message queue.");
        }
        if (string.IsNullOrEmpty(message.MessageDescription))
        {
            return BadRequest("The MessageDescription cannot be null or empty.");
        }

        // Send the ChatMessage to SQS using the injected ISQSPublisher, with SQS-specific options
        await _sqsPublisher.SendAsync(message, new SQSOptions
        {
            DelaySeconds = <delay-in-seconds>,
            MessageAttributes = <message-attributes>,
            MessageDeduplicationId = <message-deduplication-id>,
            MessageGroupId = <message-group-id>
        });

        return Ok();
    }
}
```

对于 SNS 和 EventBridge，也可以分别使用 `ISNSPublisher` 和 `IEventBridgePublisher` 来完成同样的操作。

```
await _snsPublisher.PublishAsync(message, new SNSOptions
{
    Subject = <subject>,
    MessageAttributes = <message-attributes>,
    MessageDeduplicationId = <message-deduplication-id>,
    MessageGroupId = <message-group-id>
});
```

```
await _eventBridgePublisher.PublishAsync(message, new EventBridgeOptions
{
    DetailType = <detail-type>,
    Resources = <resources>,
    Source = <source>,
    Time = <time>,
    TraceHeader = <trace-header>
});
```

默认情况下，给定类型的消息会发送到预先配置的目的地。但是，您可以使用特定于消息的发布者来覆盖单条消息的目的地。您还可以覆盖用于发布消息的底层 适用于 .NET 的 AWS SDK 客户端，这在需要根据目的地更改角色或凭证的多租户应用程序中非常有用。

```
await _sqsPublisher.SendAsync(message, new SQSOptions
{
    OverrideClient = <override IAmazonSQS client>,
    QueueUrl = <override queue URL>
});
```

# 借助 AWS Message Processing Framework for .NET 来使用消息
<a name="msg-proc-fw-consume"></a>

AWS Message Processing Framework for .NET 使您能够使用通过该框架或其中一个消息收发服务[发布的](msg-proc-fw-publish.md)消息。这些消息可以通过多种方式使用，其中一些方式如下所述。

## 消息处理程序
<a name="handle-a-message"></a>

要使用消息，请使用 `IMessageHandler` 接口为要处理的每种消息类型实施消息处理程序。消息类型和消息处理程序之间的映射是在项目启动时配置的。

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET
        services.AddAWSMessageBus(builder =>
        {
            // Register an SQS Queue that the framework will poll for messages.
            // NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");

            // Register all IMessageHandler implementations with the message type they should process. 
            // Here messages that match our ChatMessage .NET type will be handled by our ChatMessageHandler
            builder.AddMessageHandler<ChatMessageHandler, ChatMessage>();
        });
    })
    .Build()
    .RunAsync();
```

以下代码显示了 `ChatMessage` 消息的示例消息处理程序。

```
public class ChatMessageHandler : IMessageHandler<ChatMessage>
{
    public Task<MessageProcessStatus> HandleAsync(MessageEnvelope<ChatMessage> messageEnvelope, CancellationToken token = default)
    {
        // Add business and validation logic here.
        if (messageEnvelope == null)
        {
            return Task.FromResult(MessageProcessStatus.Failed());
        }

        if (messageEnvelope.Message == null)
        {
            return Task.FromResult(MessageProcessStatus.Failed());
        }

        ChatMessage message = messageEnvelope.Message;

        Console.WriteLine($"Message Description: {message.MessageDescription}");

        // Return success so the framework will delete the message from the queue.
        return Task.FromResult(MessageProcessStatus.Success());
    }
}
```

外部 `MessageEnvelope` 包含框架使用的元数据。它的 `message` 属性是消息类型（在本例中为 `ChatMessage`）。

您可以返回 `MessageProcessStatus.Success()` 以表明消息已成功处理，框架将从 Amazon SQS 队列中删除该消息。返回 `MessageProcessStatus.Failed()` 时，消息将保留在队列中，以便再次得到处理或移至[死信队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html)（如果已配置）。

## 在长时间运行的进程中处理消息
<a name="long-running-process"></a>

您可以使用 SQS 队列 URL 调用 `AddSQSPoller` 以启动一个长时间运行的 [https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice)，该服务将持续轮询队列并处理消息。

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET
        services.AddAWSMessageBus(builder =>
        {
            // Register an SQS Queue that the framework will poll for messages.
            // NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", options => 
            {
                // The maximum number of messages from this queue that the framework will process concurrently on this client.
                options.MaxNumberOfConcurrentMessages = 10;

                // The duration each call to SQS will wait for new messages.
                options.WaitTimeSeconds = 20; 
            });

            // Register all IMessageHandler implementations with the message type they should process.
            builder.AddMessageHandler<ChatMessageHandler, ChatMessage>();
        });
    })
    .Build()
    .RunAsync();
```

### 配置 SQS 消息轮询器
<a name="config-msg-poller"></a>

SQS 消息轮询器可在调用 `AddSQSPoller` 时通过 `SQSMessagePollerOptions` 进行配置。
+ `MaxNumberOfConcurrentMessages` - 队列中可同时处理的最大消息数量。默认值是 10。
+ `WaitTimeSeconds` - `ReceiveMessage` SQS 调用在返回前等待队列中消息到达的时间（以秒为单位）。如果消息可用，则调用会早于 `WaitTimeSeconds` 返回。默认值为 20。

**消息可见性超时处理**

SQS 消息有[可见性超时](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)时段。当一个使用者开始处理给定消息时，该消息仍会保留在队列中，但对其他使用者不可见，以避免重复处理。如果消息在再次可见之前未被处理和删除，则其他使用者可能会尝试处理同一条消息。

该框架将跟踪并尝试延长其当前正在处理的消息的可见性超时时间。您可以在调用 `AddSQSPoller` 时在 `SQSMessagePollerOptions` 上配置此行为。
+ `VisibilityTimeout` - 接收到的消息对后续检索请求不可见的时长（以秒为单位）。默认值为 30。
+ `VisibilityTimeoutExtensionThreshold` - 当消息的可见性超时时间距离到期剩余此秒数时，框架将延长可见性超时时间（再增加 `VisibilityTimeout` 秒）。默认值是 5。
+ `VisibilityTimeoutExtensionHeartbeatInterval` - 框架检查距离过期时间不足 `VisibilityTimeoutExtensionThreshold` 秒的消息并延长其可见性超时时间的频率（以秒为单位）。默认值是 1。

 在以下示例中，框架将每 1 秒检查一次仍在处理中的消息。对于在 5 秒内会再次可见的消息，框架会自动将每条消息的可见性超时时间再延长 30 秒。

```
// NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", options => 
{
    options.VisibilityTimeout = 30;
    options.VisibilityTimeoutExtensionThreshold = 5;
    VisibilityTimeoutExtensionHeartbeatInterval = 1;
});
```

## 在 AWS Lambda 函数中处理消息
<a name="lambda-functions"></a>

您可以将 AWS Message Processing Framework for .NET 和 [SQS 与 Lambda 的集成](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html)结合使用。这是由 `AWS.Messaging.Lambda` 软件包提供的。请参阅其[自述文件](https://github.com/aws/aws-dotnet-messaging/blob/main/src/AWS.Messaging.Lambda/README.md)开始使用。

# 将 FIFO 与 AWS Message Processing Framework for .NET 结合使用
<a name="msg-proc-fw-fifo"></a>

对于消息排序和消息重复数据删除至关重要的使用案例，AWS Message Processing Framework for .NET 支持先进先出（FIFO）[Amazon SQS 队列](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-fifo-queues.html)和 [Amazon SNS 主题](https://docs.aws.amazon.com/sns/latest/dg/sns-fifo-topics.html)。

## 发布
<a name="mpf-fifo-publish"></a>

向 FIFO 队列或主题发布消息时，您必须设置消息组 ID，该 ID 指定消息所属的组。组内的消息按顺序处理。您可以在特定于 SQS 和 SNS 的消息发布者上进行此设置。

```
await _sqsPublisher.PublishAsync(message, new SQSOptions
{
    MessageDeduplicationId = <message-deduplication-id>,
    MessageGroupId = <message-group-id>
});
```

## 订阅
<a name="mpf-fifo-subscribe"></a>

处理来自 FIFO 队列的消息时，框架会按照每次 `ReceiveMessages` 调用的接收顺序处理给定消息组中的消息。当配置以 `.fifo` 结尾的队列时，框架会自动进入此操作模式。

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET.
        services.AddAWSMessageBus(builder =>
        {
            // Because this is a FIFO queue, the framework automatically handles these messages in order.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MPF.fifo");
            builder.AddMessageHandler<OrderMessageHandler, OrderMessage>();
        });
    })
    .Build()
    .RunAsync();
```

# AWS Message Processing Framework for .NET 的日志记录和 OpenTelemetry
<a name="msg-proc-fw-telemetry"></a>

AWS Message Processing Framework for .NET 已集成 OpenTelemetry，可记录该框架发布或处理的每条消息的[追踪信息](https://opentelemetry.io/docs/concepts/signals/traces/)。这是由 [https://www.nuget.org/packages/AWS.Messaging.Telemetry.OpenTelemetry](https://www.nuget.org/packages/AWS.Messaging.Telemetry.OpenTelemetry) 软件包提供的。请参阅其[自述文件](https://github.com/aws/aws-dotnet-messaging/blob/main/src/AWS.Messaging.Telemetry.OpenTelemetry/README.md)开始使用。

**注意**  
有关日志记录的安全性信息，请参阅[AWS Message Processing Framework for .NET 的安全性](msg-proc-fw-security.md)。

# 自定义 AWS Message Processing Framework for .NET
<a name="msg-proc-fw-customize"></a>

AWS Message Processing Framework for .NET 在三个不同的“层”中构建、发送和处理消息：

1. 在最外层，框架构建特定于服务的 AWS 原生请求或响应。例如，在 Amazon SQS 中，它生成 [https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html) 请求并处理该服务定义的 [https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html) 对象。

1. 在 SQS 请求和响应中，框架将 `MessageBody` 元素（对于 Amazon SNS，为 `Message`，对于 Amazon EventBridge，为 `Detail`）设置为 [JSON 格式的 CloudEvent](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/json-format.md)。它包含框架设置的元数据，在处理消息时可在 `MessageEnvelope` 对象上访问。

1. 在最内层，CloudEvent JSON 对象内的 `data` 属性包含一个 JSON 序列化表示的 .NET 对象，该对象作为消息发送或接收。

   ```
   {
       "id":"b02f156b-0f02-48cf-ae54-4fbbe05cffba",
       "source":"/aws/messaging",
       "specversion":"1.0",
       "type":"Publisher.Models.ChatMessage",
       "time":"2023-11-21T16:36:02.8957126+00:00",
       "data":"<the ChatMessage object serialized as JSON>"
   }
   ```

您可以自定义消息信封的配置方式和读取方式：
+ `"id"` 是消息的唯一标识符。它默认设置为新的 GUID，但是您可以实施自己的 `IMessageIdGenerator` 并将其注入 DI 容器来覆盖此设置。
+ `"type"` 控制消息路由到处理程序的方式。默认情况下，此设置使用与消息对应的 .NET 类型的完整名称。当通过 `AddSQSPublisher`、`AddSNSPublisher` 或 `AddEventBridgePublisher` 将消息类型映射到目的地时，您可以通过 `messageTypeIdentifier` 参数覆盖此设置。
+ `"source"` 表示发送消息的系统或服务器。
  + 如果从 AWS Lambda 发布，则该值是函数名称；如果在 Amazon ECS 上，则该值为集群名称和任务 ARN；如果在 Amazon EC2 上，则该值为实例 ID，否则为 `/aws/messaging` 的回退值。
  + 您可以在 `MessageBusBuilder` 上通过 `AddMessageSource` 或 `AddMessageSourceSuffix` 覆盖此设置。
+ `"time"` 设置为当前的日期时间（UTC）。您可以通过实施自己的 `IDateTimeHandler` 并将其注入 DI 容器来覆盖此设置。
+ `"data"` 包含作为消息发送或接收的 .NET 对象的 JSON 表示：
  + `MessageBusBuilder` 上的 `ConfigureSerializationOptions` 让您能够配置序列化和反序列化消息时将使用的 [https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions)。
  + 要注入其他属性或在框架构建消息信封后对其进行转换，您可以实施 `ISerializationCallback` 并通过 `MessageBusBuilder` 上的 `AddSerializationCallback` 注册它。

# AWS Message Processing Framework for .NET 的安全性
<a name="msg-proc-fw-security"></a>

AWS Message Processing Framework for .NET 依赖适用于 .NET 的 AWS SDK 与 AWS 进行通信。有关 适用于 .NET 的 AWS SDK 中安全性的更多信息，请参阅[本 AWS 产品或服务的安全性](security.md)。

出于安全考虑，该框架不记录用户发送的数据消息。如果要出于调试目的启用此功能，则需要在 Message Bus 中调用 `EnableDataMessageLogging()`，如下所示：

```
builder.Services.AddAWSMessageBus(bus =>
{
    builder.EnableDataMessageLogging();
});
```

如果您发现潜在的安全问题，请参阅[安全策略](https://github.com/aws/aws-dotnet-messaging/security/policy)以获取报告信息。

# 在 适用于 .NET 的 AWS SDK 中将 AWS 与 .NET Aspire 集成
<a name="aspire-integrations"></a>

.NET Aspire 是一种构建云就绪型应用程序的新方法。它专门为本地环境提供了一种编排工具，用于运行、连接和调试分布式应用程序的组件。为了改善云就绪型应用程序的内部开发循环，我们构建了与 .NET Aspire 的集成，用于将 .NET 应用程序连接到 AWS 资源。这些集成可通过 [Aspire.Hosting.AWS](https://www.nuget.org/packages/Aspire.Hosting.AWS) NuGet 软件包获取。

以下.NET Aspire 集成可用：
+ 通过 [CloudFormation](https://aws.amazon.com/cloudformation/) 预置 AWS 资源的功能。这种集成可用于 .NET Aspire AppHost 项目。

  有关更多信息，请参阅博客文章 [Integrating AWS with .NET Aspire](https://aws.amazon.com/blogs/developer/integrating-aws-with-net-aspire/)。
+ 安装和配置 适用于 .NET 的 AWS SDK 并将其连接到 [Amazon DynamoDB local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocalHistory.html)。这种集成可用于 .NET Aspire AppHost 项目。

  有关更多信息，请参阅博客文章 [Integrating AWS with .NET Aspire](https://aws.amazon.com/blogs/developer/integrating-aws-with-net-aspire/)。
+ 为 [AWS Lambda](https://aws.amazon.com/lambda/) 函数启用本地开发环境。这种集成可用于 .NET Aspire AppHost 项目。

  有关更多信息，请参阅博客文章 [Building and Debugging .NET Lambda applications with .NET Aspire (Part 1)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-1/) 和 [Building and Debugging .NET Lambda applications with .NET Aspire (Part 2)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-2/)。
**注意**  
这是适用于预览版中特征的预发行文档。本文档随时可能更改。

  由于此功能为预览版，因此您需要选择加入来启用预览功能。有关此预览功能以及如何选择加入的更多信息，请参阅 [GitHub 上的开发跟踪器问题](https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17)。

## 其他信息
<a name="aspire-integrations-additional"></a>

有关 [Aspire.Hosting.AWS](https://www.nuget.org/packages/Aspire.Hosting.AWS) 中提供的集成的更多信息以及详细使用说明，请参阅以下资源。
+ 博客文章 [Integrating AWS with .NET Aspire](https://aws.amazon.com/blogs/developer/integrating-aws-with-net-aspire/)。
+ 博客文章 [Building and Debugging .NET Lambda applications with .NET Aspire (Part 1)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-1/) 和 [Building and Debugging .NET Lambda applications with .NET Aspire (Part 2)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-2/)。
+ GitHub 上的 [integrations-on-dotnet-aspire-for-aws](https://github.com/aws/integrations-on-dotnet-aspire-for-aws) 存储库。
+ [Aspire.Hosting.AWS](https://www.nuget.org/packages/Aspire.Hosting.AWS) NuGet 软件包的详细[自述文件](https://github.com/aws/integrations-on-dotnet-aspire-for-aws/blob/main/src/Aspire.Hosting.AWS/README.md)。

# 对其它 AWS 服务和配置的支持
<a name="other-apis-intro"></a>

除前面各部分中描述的服务外，适用于 .NET 的 AWS SDK还支持 AWS 服务。有关所有支持服务的 API 的信息，请参阅[适用于 .NET 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)。

除了各个 AWS 服务的命名空间之外，适用于 .NET 的 AWS SDK还提供了以下 API：


****  

| 区域图 | 描述 | 资源 | 
| --- | --- | --- | 
|  AWS Support  |  以编程方式访问 AWS Support 案例和 Trusted Advisor 功能。  |  请参阅 [Amazon.AWSSupport](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/AWSSupport/NAWSSupport.html) 和 [Amazon.AWSSupport.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/AWSSupport/NAWSSupportModel.html)。  | 
|  常规  |  帮助程序类和枚举。  |  请参阅 [Amazon](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Amazon/N.html) 和 [Amazon.Util](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Util/NUtil.html)。  | 