Microsoft Graph .NETでDriveItemのチェックアウトをバージョンを増やさずに取り消す方法【discardCheckout】

Microsoft Graph SDK for .NET で OneDrive / SharePoint のファイルをチェックアウトしたものの、「やっぱり元に戻したい、でもバージョン履歴は汚したくない」という場面はよくあります。この記事では、従来の「チェックイン+バージョン復元」ではなく、driveItem: discardCheckout を使ってスマートにチェックアウトを取り消す方法と、その実装・運用のポイントを詳しく解説します。

目次

OneDrive / SharePoint の「チェックアウト」とは

まずは前提として、OneDrive / SharePoint ドキュメントライブラリにおける「チェックアウト」の挙動を軽く整理しておきます。特に、バージョン管理とどう関係するかを理解しておくと、discardCheckout の役割がイメージしやすくなります。

チェックアウト・チェックイン・チェックアウトの破棄の違い

操作英語UI主な役割バージョン履歴への影響
チェックアウトCheck out自分専用の編集ロックをかける。ほかのユーザーの変更を防ぎつつ編集する。チェックアウト時点では通常、新しいバージョンは増えない。
チェックインCheck in編集を確定してほかのユーザーにも公開する。チェックインのタイミングで新しいバージョンが 1 つ作成される。
チェックアウトの破棄Discard check outチェックアウト中の編集中身を破棄し、直前のチェックイン状態に戻す。通常、新しいバージョンは作成されない。編集内容は消える。

UI から操作する場合は、「…(その他)」メニューやリボンから「チェックアウトの破棄」を選ぶだけで済みますが、アプリケーションから同じことをやろうとすると、これまでは少し厄介でした。

課題:バージョンを増やさずにチェックアウトを取り消したい

.NET の Microsoft Graph SDK を使って DriveItem(OneDrive / SharePoint のファイル)を扱うケースを考えます。よくある要件は次のようなものです。

  • OneDrive / SharePoint 上のファイルを Graph API 経由でチェックアウト・編集している。
  • ユーザーが「やっぱりこの変更はいらない」と判断したとき、チェックアウトだけを取り消したい。
  • その際、「余計なバージョンを増やしたくない」=履歴を汚したくない。

しかし、古い実装パターンでは、チェックアウトを取り消す明示的な API が無かったため、次のようなワークアラウンドが使われていました。

  1. 一度 チェックイン する。
  2. 直前のバージョンに対して .RestoreVersion()(バージョンの復元) を呼び出す。

この方法には、次の問題があります。

  • 「チェックイン」自体が新しいバージョンを 1 つ作る。
  • .RestoreVersion() も、「復元した」という新しいバージョンを 1 つ作る仕様。
  • 結果として、履歴に不要なバージョンが 2 つも追加されてしまう。
手法手順増えるバージョン数デメリット
従来のワークアラウンドチェックイン → .RestoreVersion()2履歴がノイズで増え、監査やトラブルシュートがしにくくなる。
discardCheckoutチェックアウトを破棄0(通常)チェックアウト中の編集内容は完全に失われる。

バージョン履歴をきれいに保ちたい場合や、運用ポリシーでバージョン数をなるべく抑えたいようなケースでは、従来のワークアラウンドはどうしても扱いにくいのが実情でした。

解決策:driveItem: discardCheckout を使う

現在は、Microsoft Graph v1.0 に driveItem: discardCheckout が正式に追加されており、これを使うことで、UI と同じ「チェックアウトの破棄」動作を API 経由で実現できます。

discardCheckout の動作イメージ

discardCheckout は、次のような動きをする API です。

  • 対象の DriveItem がチェックアウトされている前提。
  • チェックアウト中に行った編集内容を破棄する。
  • 直前のチェックイン済みバージョンの内容に戻す。
  • 通常、新しいバージョンは作られないため、履歴が増えない。

図にすると、以下のようなイメージです。

状態説明
v3(チェックイン済み)最後に確定された状態。履歴に残っている。
v3(チェックアウト中)ユーザーが編集中。まだチェックインしていない。
discardCheckout 呼び出しチェックアウト中の編集内容を破棄し、v3 チェックイン済みの内容に巻き戻す。
結果履歴の最新は引き続き v3 のまま。新しい v4 は作られない。

つまり、「誤ってチェックアウトした」「ここまでの編集は投げ捨ててやり直したい」といったケースでは、discardCheckout を使うことが正攻法になります。

過去との違い(背景)

  • 2023 年頃までは Graph API に「チェックアウトの破棄」用エンドポイントが存在せず、コミュニティ Q&A でも「未提供なので要望登録を」といった回答が主流でした。
  • その後のアップデートで、driveItem: discardCheckout が v1.0 に追加され、OneDrive / SharePoint 両方に対して使えることが明文化されました。
  • これにより、従来のようなワークアラウンドに頼らず、UI と同じ操作を API から正しく再現できるようになりました。

以降では、この discardCheckout を REST API 経由で呼び出す方法と、.NET の Microsoft Graph SDK で実装する方法を順番に見ていきます。

REST API での discardCheckout 呼び出し

基本のエンドポイント

REST で呼び出す場合、エンドポイントはとてもシンプルです。ユーザー自身の OneDrive の場合は次のようになります。

POST https://graph.microsoft.com/v1.0/me/drive/items/{item-id}/discardCheckout
  • HTTP メソッド:POST
  • リクエストボディ:不要(空でよい)
  • 成功時のステータスコード:204 No Content

SharePoint サイトや別のドライブを対象にする場合は、ドライブのルートを指定する部分を変えるだけです。

POST https://graph.microsoft.com/v1.0/drives/{drive-id}/items/{item-id}/discardCheckout

POST https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}/discardCheckout

典型的な HTTP レスポンス

HTTP ステータス状況備考
204 No Contentチェックアウトの破棄に成功レスポンスボディなし。特に JSON は返ってこない。
400 Bad Request対象ファイルがチェックアウトされていない「チェックアウト中ではないので破棄できない」といったエラーになる。
423 Locked別ユーザーがチェックアウトしている委任アクセス(ユーザーの権限)では、他人のチェックアウトを勝手に破棄できない。
403 Forbidden権限不足必要な Graph 権限が付与されていない、または条件を満たしていない。
404 Not FoundDriveItem が存在しないURL の ID が誤っている、または削除済みなど。

REST を直接叩く場合も、後述する .NET SDK を使う場合も、このステータスコードのパターンを意識してエラーハンドリングを設計すると堅牢な実装になります。

.NET Microsoft Graph SDK での discardCheckout 実装例

ここからは、本題である .NET の Microsoft Graph SDK を使った実装例を紹介します。SDK のバージョン 5.x 世代では、公式ドキュメントのスニペットとして、次のようなコードが案内されています。

最小のコード例

すでに GraphServiceClient が初期化済みである前提で、特定の DriveItem のチェックアウトを破棄するコードは次のようになります。

using Microsoft.Graph;

public async Task DiscardCheckoutAsync(GraphServiceClient graphClient, string driveId, string itemId)
{
    await graphClient
        .Drives[driveId]
        .Items[itemId]
        .DiscardCheckout
        .PostAsync();
}
  • driveId:対象となる OneDrive / SharePoint ドライブの ID。
  • itemId:対象となるファイル(DriveItem)の ID。

await が完了すると、その DriveItem に対するチェックアウトは破棄され、直前のチェックイン状態に戻ります。戻り値はなく(Task)、成功時は例外も発生しません。

エラーハンドリング付きの例

次に、よくあるエラーパターンをハンドリングした実装例を示します。実際には SDK が投げる例外型に応じて処理を分岐させる設計がおすすめです。

using Microsoft.Graph;
using Microsoft.Graph.Models.ODataErrors;

public async Task<bool> TryDiscardCheckoutAsync(
    GraphServiceClient graphClient,
    string driveId,
    string itemId)
{
    try
    {
        await graphClient
            .Drives[driveId]
            .Items[itemId]
            .DiscardCheckout
            .PostAsync();

        return true; // 成功
    }
    catch (ODataError ex)
    {
        // ex.Error.Code や ex.Error.Message をログに残す
        // 例: "checkoutNotFound", "locked", "accessDenied" など

        // 状況に応じて false を返すなど
        return false;
    }
}

例外の中身(ex.Error.Code や ex.Error.Message)をログに残しておくと、運用時に「ファイルがチェックアウトされていなかった」「他ユーザーがロックしていた」などの状況が簡単に分析できます。

「現在のユーザーの OneDrive」向けの呼び出し例

ユーザー自身の OneDrive を前提にする場合は、Me.Drive を使った次のような書き方も可能です。

public async Task DiscardMyDriveItemCheckoutAsync(GraphServiceClient graphClient, string itemId)
{
    await graphClient
        .Me
        .Drive
        .Items[itemId]
        .DiscardCheckout
        .PostAsync();
}

Azure AD の認証で「サインインユーザーの OneDrive のみ」を扱うようなアプリケーションでは、このように Me ベースで書いておくとコードの意図が分かりやすくなります。

PowerShell からチェックアウトを破棄する(参考)

.NET の実装とは直接関係しませんが、運用・デバッグの観点からは PowerShell で同等の操作ができると便利です。Microsoft Graph PowerShell SDK には、Remove-MgUserDriveItemCheckout というコマンドレットが用意されています。

Connect-MgGraph -Scopes "Files.ReadWrite"

Remove-MgUserDriveItemCheckout `
    -UserId <UPN or UserId> `
    -DriveItemId <itemId>
  • アプリの動作を検証したいときに、PowerShell から手動でチェックアウトを破棄して動作確認したり、トラブル時の緊急対応に使ったりすることができます。
  • 権限やエラーコードの挙動を事前に PowerShell で確認してから .NET 実装に落とし込むと、開発効率が上がります。

必要な Graph 権限とアクセス種別

driveItem: discardCheckout を呼び出すには、適切な Graph 権限が必要です。「最小特権の原則」を踏まえて、シナリオ別に整理しておきましょう。

代表的な権限の組み合わせ

アクセス種別想定シナリオ代表的な必要権限備考
委任(Delegated)ユーザーが自分の OneDrive ファイルを扱う Web アプリFiles.ReadWriteユーザー自身がチェックアウトしているファイルに対して破棄が可能。
委任(Delegated)組織内の複数ユーザーのファイルをまとめて扱う管理ツールFiles.ReadWrite.All または Sites.ReadWrite.All権限は広いが、実際に破棄できるのは「本人がチェックアウトしたファイル」に限られる。
アプリケーション(App-only)バックグラウンドでサイトコレクション全体をメンテナンスするバッチFiles.ReadWrite.All または Sites.ReadWrite.All適切に権限が付与されていれば、他ユーザーがチェックアウトしたファイルを破棄することも可能。

特に重要なのは次の 2 点です。

  • 委任アクセスの場合、原則として「チェックアウトした本人」しか自分のチェックアウトを破棄できません。
  • アプリケーション権限を使う場合は、設計次第で他ユーザーのチェックアウトを強制的に破棄することも可能になるため、誰がどういう条件で実行できるかを運用ポリシーでしっかり定めておく必要があります。

エラーパターンと実践的なハンドリング

実運用では、「常にきれいに 204 が返ってくる」とは限りません。よくあるエラーパターンを整理し、どう対処するかを考えておきましょう。

代表的なエラーパターン

ステータス典型的な原因対処の考え方
400 Bad Request対象の DriveItem がチェックアウト状態ではない事前に「チェックアウト中かどうか」を確認するか、ユーザーに「すでにチェックアウトは解除されています」とメッセージを返す。
423 Locked他ユーザーがチェックアウトしている委任アクセスの場合はそのユーザー本人から実行してもらうか、アプリケーション権限と運用手順を検討する。
403 Forbidden必要な Graph 権限がない、またはサイト/アイテムへのアクセス権がないアプリ登録の権限設定、ユーザー/グループの SharePoint 権限を見直す。
404 Not FoundDriveItem が削除済み、または ID が誤りログに itemId と driveId を出力して調査。削除済みであればユーザーに通知する。

.NET 側でのハンドリング例

実務では、「失敗したら即エラー終了」ではなく、「想定済みのエラーかどうか」で分岐させると UX がよくなります。例えば次のようなパターンです。

public async Task DiscardCheckoutWithMessageAsync(
    GraphServiceClient graphClient,
    string driveId,
    string itemId)
{
    try
    {
        await graphClient
            .Drives[driveId]
            .Items[itemId]
            .DiscardCheckout
            .PostAsync();

        Console.WriteLine("チェックアウトを破棄しました。");
    }
    catch (ODataError ex)
    {
        var code = ex.Error?.Code ?? string.Empty;

        if (code.Equals("checkoutNotFound", StringComparison.OrdinalIgnoreCase))
        {
            Console.WriteLine("このファイルはチェックアウトされていません。");
        }
        else if (code.Equals("locked", StringComparison.OrdinalIgnoreCase))
        {
            Console.WriteLine("他のユーザーによりチェックアウトされています。");
        }
        else
        {
            Console.WriteLine($"チェックアウトの破棄に失敗しました: {ex.Error?.Message}");
        }
    }
}

実際のエラーコード名は環境や実装により異なる場合がありますが、このように「よくあるケースを想定してメッセージを変える」だけでも、運用担当者やエンドユーザーにとって理解しやすい挙動になります。

従来のワークフローから discardCheckout への移行

すでに「チェックイン → .RestoreVersion()」を使ったワークアラウンド実装が存在する場合、どのように discardCheckout に移行すべきでしょうか。具体的なステップに分解してみます。

移行ステップ

  1. 既存のワークアラウンド箇所を洗い出す
    ソリューション全体で RestoreVersion を検索し、「チェックアウト取り消し目的」で使っている箇所を特定します。
  2. UI 仕様を確認する
    ユーザーにとって「チェックアウトの破棄」はどのようなボタンやメニューで提供されているかを見直し、その背後のロジックを discardCheckout ベースに差し替えます。
  3. バージョン履歴を前提としたロジックの有無を確認する
    「復元されたこと自体をイベントとして扱う」ような特殊な処理がないかを確認し、必要に応じて監査ログや独自ログに置き換えます。
  4. ステージング環境での動作検証
    複数ユーザーで同時にファイルを編集するシナリオや、履歴が大量にあるファイルでの挙動をテストします。

Before / After の比較

観点旧実装(チェックイン+復元)新実装(discardCheckout)
バージョン履歴不要なバージョンが 2 つ増える通常、増えない
ユーザーの理解しやすさ履歴が増えて「どれが本当の最新か」直感的に分かりにくいUI の「チェックアウトの破棄」と同じ動作のため分かりやすい
監査・トラブルシュートノイズが多く、意図しない復元との区別がつきにくい履歴がシンプルになり、監査が容易
実装のシンプルさ2 ステップ+バージョン指定が必要1 API 呼び出しで完結

このように、discardCheckout への移行は、コードの見通しだけでなく運用の観点でもメリットが大きいと言えます。

運用で気をつけたいポイントとベストプラクティス

最後に、discardCheckout を実運用で使う際に意識しておきたいポイントと、現場で役立つベストプラクティスをいくつか紹介します。

本当に破棄してよいかの確認

  • discardCheckout は、「未保存の変更を捨てる」性質上、一度実行すると編集内容は戻せません。
  • UI 上でボタンを用意する場合は、確認ダイアログを挟む、変更が大きい場合は「バックアップとして別ファイルにコピーしてから破棄する」といった工夫も検討できます。

誰がいつ破棄したかを記録する

  • バージョン履歴は増えませんが、その分、「いつ」「誰が」「どのファイルのチェックアウトを破棄したか」は別途ログに残しておくのが無難です。
  • アプリ側で、user id と driveId / itemId、実行時刻をアプリケーションログや監査用ストレージに保存する設計をおすすめします。

強制的な破棄は慎重に

  • アプリケーション権限を使えば、他ユーザーのチェックアウトを強制的に破棄することも可能です。
  • しかし、他人の作業を消すことになるため、「長期間チェックインされていない場合のみ」「管理者が明示的に指示した場合のみ」など、厳格な条件と手順を定めたうえで実装すべきです。

チェックアウト前提のライブラリ設定との相性

  • SharePoint のドキュメントライブラリ設定によっては、「編集前に必ずチェックアウトが必要」とされている場合があります。
  • そのようなライブラリでは、discardCheckout を「やり直しボタン」のように活用できる一方で、頻繁に破棄しすぎると「いつまでも変更が確定されない」状態になります。
  • 一定回数以上破棄したら警告する、破棄の多いファイルをレポートする、などの運用も検討するとよいでしょう。

サンプルシナリオ:誤編集をキャンセルする UI の実装

最後に、実際のアプリケーションでありがちなシナリオを例に、discardCheckout を組み込んだ実装例を紹介します。

シナリオ

  • Web アプリから OneDrive / SharePoint のファイルを編集している。
  • 編集開始時に自動でチェックアウトする。
  • ユーザーが「キャンセル」ボタンを押したら、discardCheckout で変更を破棄したい。

実装の流れ

  1. ファイル編集画面を開くときに、DriveItem がチェックアウトされていなければチェックアウトする。
  2. ユーザーが「保存」ボタンを押した場合は、通常どおりチェックインする。
  3. ユーザーが「キャンセル」ボタンを押した場合は、discardCheckout を呼び出す。
  4. 成功したら「変更を破棄しました」といったメッセージを表示し、元の一覧画面に戻す。

コード例(概略)

public async Task CancelEditAsync(GraphServiceClient graphClient, string driveId, string itemId)
{
    // 本当に破棄してよいか、UI 側で確認ダイアログを出している前提

    // チェックアウトの破棄を試みる
    var success = await TryDiscardCheckoutAsync(graphClient, driveId, itemId);

    if (success)
    {
        // ログ出力
        Console.WriteLine($"DriveItem {itemId} のチェックアウトを破棄しました。");
    }
    else
    {
        // 状況に応じてメッセージを表示
        Console.WriteLine("チェックアウトの破棄に失敗しました。すでにチェックアウトされていない可能性があります。");
    }
}

このように、「チェックアウトの破棄」を UI の「キャンセル」操作と紐づけると、ユーザーにとって直感的で分かりやすいアプリケーションに仕上がります。

まとめ:今は discardCheckout を使うのが正攻法

かつては、Graph API の制約から「チェックイン → .RestoreVersion()」という苦しいワークアラウンドに頼らざるを得ませんでしたが、現在は driveItem: discardCheckout が正式に提供されており、バージョンを増やさずにチェックアウトを取り消すことができます。

  • 履歴をきれいに保ちたい。
  • UI の「チェックアウトの破棄」と同じ動作を API から行いたい。
  • .NET でシンプルなコードにしたい。

こうした要件がある場合は、古いワークアラウンドは早めに廃止し、discardCheckout を前提とした設計に移行することを強くおすすめします。権限設定やエラーハンドリング、運用ポリシーをきちんと整理すれば、OneDrive / SharePoint のバージョン管理をよりクリーンかつ安全に自動化できるはずです。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次