SqlDataSource でデータを挿入、更新、削除する (VB)

作成者: Scott Mitchell

PDF のダウンロード

前のチュートリアルでは、ObjectDataSource コントロールでデータの挿入、更新、および削除を許可する方法について説明しました。 SqlDataSource コントロールは同じ操作をサポートしていますが、この方法は異なります。このチュートリアルでは、データを挿入、更新、削除するように SqlDataSource を構成する方法について説明します。

はじめに

挿入、更新、および削除の概要」で説明したように、GridView コントロールには組み込みの更新および削除機能が用意されていますが、DetailsView コントロールと FormView コントロールには、編集および削除機能と共にサポートの挿入が含まれます。 これらのデータ変更機能は、コード行を記述しなくても、データ ソース管理に直接接続できます。 挿入、更新、および削除の概要は、GridView、DetailsView、および FormView コントロールを使用して挿入、更新、および削除を容易にするために ObjectDataSource を使用して調べました。 または、ObjectDataSource の代わりに SqlDataSource を使用することもできます。

ObjectDataSource を使用して挿入、更新、削除をサポートするには、挿入、更新、または削除アクションを実行するために呼び出すオブジェクト レイヤー メソッドを指定する必要があることを思い出してください。 SqlDataSource では、、、および DELETE SQL ステートメント (またはストアド プロシージャ) を指定INSERTUPDATEして実行する必要があります。 このチュートリアルで説明するように、これらのステートメントは手動で作成することも、SqlDataSource のデータ ソースの構成ウィザードによって自動的に生成することもできます。

注意

GridView、DetailsView、および FormView コントロールの挿入、編集、および削除機能については既に説明しているので、このチュートリアルでは、これらの操作をサポートするように SqlDataSource コントロールを構成することに重点を置きます。 GridView、DetailsView、および FormView 内でこれらの機能の実装をブラッシュアップする必要がある場合は、「挿入、更新、および 削除の概要」から始まる「データの編集、挿入、および削除」のチュートリアルに戻ります。

手順 1: INSERT、UPDATE、および DELETE ステートメントを指定する

過去 2 つのチュートリアルで説明したように、SqlDataSource コントロールからデータを取得するには、次の 2 つのプロパティを設定する必要があります。

  1. ConnectionStringは、クエリを送信するデータベースを指定し、
  2. SelectCommandは、結果を返すために実行するアドホック SQL ステートメントまたはストアド プロシージャ名を指定します。

パラメーターを持つ値の場合 SelectCommand 、パラメーター値は SqlDataSource の SelectParameters コレクションを介して指定され、ハードコーディングされた値、共通のパラメーター ソース値 (querystring フィールド、セッション変数、Web コントロール値など) を含めたり、プログラムで割り当てることができます。 SqlDataSource コントロールの Select() メソッドがプログラムまたはデータ Web コントロールから自動的に呼び出されると、データベースへの接続が確立されると、パラメーター値がクエリに割り当てられ、コマンドがデータベースにシャトルされます。 その後、コントロールの DataSourceMode プロパティの値に応じて、DataSet または DataReader として結果が返されます。

データの選択に加えて、SqlDataSource コントロールを使用して、および SQL ステートメントをほとんど同じ方法で指定INSERTUPDATEすることで、データの挿入、更新、削除DELETEを行うことができます。 実行する 、UpdateCommandInsertCommandおよび DeleteCommand の各プロパティをINSERT、、UPDATEおよび DELETE SQL ステートメントに割り当てるだけです。 ステートメントにパラメーターがある場合 (ほとんどの場合と同様)、、UpdateParametersおよび DeleteParameters コレクションにInsertParameters含めます。

InsertCommandUpdateCommand、または DeleteCommand の値を指定すると、対応するデータ Web コントロールのスマート タグの [挿入の有効化]、[編集の有効化]、または [削除の有効化] オプションが使用できるようになります。 これを説明するために、SqlDataSource コントロールを使用したデータのクエリに関するチュートリアルで作成したページのQuerying.aspx例を見て、削除機能を含むように拡張します。

まず、 フォルダーから ページと Querying.aspx ページをSqlDataSourceInsertUpdateDelete.aspxきます。 ページのDesignerからQuerying.aspx、最初の例 (ProductsDataSourceおよび コントロール) から SqlDataSource と GridView1 GridView を選択します。 2 つのコントロールを選択したら、[編集] メニューに移動し、[コピー] を選択します (または Ctrl + C キーを押します)。 次に、 のDesignerInsertUpdateDelete.aspxに移動し、コントロールを貼り付けます。 2 つのコントロールを に移動した後、 InsertUpdateDelete.aspxブラウザーでページをテストします。 データベース テーブル内のすべてのレコードの ProductID、、および UnitPrice 列の値が表示されますProductsProductName

すべての製品が一覧表示され、ProductID 順に並べ替え

図 1: すべての製品が一覧表示され、並べ替えられます ProductID (フルサイズの画像を表示する をクリックします)

SqlDataSource の DeleteCommand プロパティと DeleteParameters プロパティの追加

この時点で、テーブルからすべてのレコードを返す SqlDataSource と、このデータを Products レンダリングする GridView があります。 この例を拡張して、ユーザーが GridView を使用して製品を削除できるようにすることが目標です。 これを実現するには、SqlDataSource コントロールの DeleteCommandDeleteParameters プロパティの値を指定し、削除をサポートするように GridView を構成する必要があります。

プロパティと DeleteParameters プロパティはDeleteCommand、さまざまな方法で指定できます。

  • 宣言型構文を使用する
  • Designerのプロパティ ウィンドウから
  • データ ソースの構成ウィザードの [カスタム SQL ステートメントまたはストアド プロシージャの指定] 画面から
  • データ ソースの構成ウィザードの [ビューのテーブルから列を指定する] 画面の [詳細設定] ボタンを使用すると、 プロパティと DeleteParameters プロパティでDeleteCommand使用される SQL ステートメントとパラメーター コレクションが実際に自動的に生成DELETEされます。

手順 2 でステートメントを自動的に作成する DELETE 方法について説明します。 ここでは、Designerのプロパティ ウィンドウを使用しましょう。ただし、[データ ソースの構成] ウィザードまたは宣言型構文オプションも同様に機能します。

のDesignerからInsertUpdateDelete.aspx、SqlDataSource をクリックProductsDataSourceし、プロパティ ウィンドウを表示します ([表示] メニューから [プロパティ ウィンドウ] を選択するか、単に F4 キーを押します)。 DeleteQuery プロパティを選択すると、省略記号のセットが表示されます。

DeleteQuery プロパティが選択された ProductsDataSource プロパティ ウィンドウを示すスクリーンショット。

図 2: [プロパティ] ウィンドウから DeleteQuery プロパティを選択する

注意

SqlDataSource には DeleteQuery プロパティがありません。 代わりに、DeleteQuery は プロパティと DeleteParameters プロパティのDeleteCommand組み合わせであり、Designerでウィンドウを表示する場合にのみ、プロパティ ウィンドウに一覧表示されます。 [ソース] ビューでプロパティ ウィンドウを見ている場合は、代わりに プロパティがDeleteCommand表示されます。

DeleteQuery プロパティの省略記号をクリックして、[コマンドとパラメーターのエディター] ダイアログ ボックスを表示します (図 3 を参照)。 このダイアログ ボックスでは、SQL ステートメントを DELETE 指定し、パラメーターを指定できます。 コマンド テキスト ボックスに次のクエリを DELETE 入力します (手動または必要に応じてクエリ ビルダーを使用)。

DELETE FROM Products
WHERE ProductID = @ProductID

次に、[パラメーターの更新] ボタンをクリックして、パラメーターを @ProductID 下のパラメーターの一覧に追加します。

delete コマンド パラメーターの一覧に <span class=@ProductID パラメーターが追加された [コマンド] ウィンドウと [パラメーター] エディター ウィンドウを示すスクリーンショット。 />

図 3: [プロパティ] ウィンドウから DeleteQuery プロパティを選択します (クリックするとフルサイズの画像が表示されます)

このパラメーターには値を指定 しないでください (パラメーター ソースは None のままにします)。 GridView に削除のサポートを追加すると、GridView は、Delete ボタンがクリックされた行のコレクションの DataKeys 値を使用して、このパラメーター値を自動的に指定します。

注意

クエリでDELETE使用されるパラメーター名は、GridView、DetailsView、または FormView の値のDataKeyNames名前と同じである必要があります。 つまり、Products テーブルのDELETE主キー列名 (したがって GridView の DataKeyNames 値) は であるため、 ステートメントの パラメーターには意図的に という名前がProductID付けられます @ProductID (たとえば、 @IDではなく)。

パラメーターの名前と DataKeyNames 値が一致しない場合、GridView はコレクションの値をパラメーターに自動的に DataKeys 割り当てることができません。

[コマンドとパラメーターのエディター] ダイアログ ボックスに削除関連情報を入力した後、[OK] をクリックし、[ソース] ビューに移動して、結果の宣言型マークアップを調べます。

<asp:SqlDataSource ID="ProductsDataSource" runat="server"
    ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
    SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice] FROM [Products]"
    DeleteCommand="DELETE FROM Products WHERE ProductID = @ProductID">
    <DeleteParameters>
        <asp:Parameter Name="ProductID" />
    </DeleteParameters>
</asp:SqlDataSource>

プロパティと セクションと DeleteCommand Parameter オブジェクトproductIDの追加に<DeleteParameters>注意してください。

削除用の GridView の構成

プロパティを DeleteCommand 追加すると、GridView のスマート タグに [削除を有効にする] オプションが含まれるようになりました。 先に進み、このチェックボックスをチェックします。 「 挿入、更新、および削除の概要」で説明したように、GridView では、その ShowDeleteButton プロパティが に設定された CommandField が True追加されます。 図 4 に示すように、ブラウザーからページにアクセスすると、[削除] ボタンが含まれます。 一部の製品を削除して、このページをテストします。

各 GridView 行に [削除] ボタンが含まれるようになりました

図 4: 各 GridView 行に [削除] ボタンが含まれるようになりました (クリックするとフルサイズの画像が表示されます)

[削除] ボタンをクリックするとポストバックが発生し、GridView は Delete ボタンがクリックされた行のコレクション値のDataKeys値をパラメーターに割り当てProductID、SqlDataSource の Delete() メソッドを呼び出します。 その後、SqlDataSource コントロールはデータベースに接続し、 ステートメントを DELETE 実行します。 その後、GridView は SqlDataSource に再バインドし、現在の製品セット (削除されたレコードは含まれない) を取得して表示します。

注意

GridView はコレクションを DataKeys 使用して SqlDataSource パラメーターを設定するため、GridView の DataKeyNames プロパティを主キーを構成する列に設定し、SqlDataSource が SelectCommand これらの列を返す必要があります。 さらに、SqlDataSource の DeleteCommand パラメーター名が に @ProductID設定されていることが重要です。 プロパティが DataKeyNames 設定されていない場合、または パラメーターに という名前 @ProductsIDが付いていない場合、[削除] ボタンをクリックするとポストバックが発生しますが、実際にはレコードは削除されません。

図 5 は、この操作をグラフィカルに示しています。 データ Web コントロールの 挿入、更新、および削除に関連するイベントのチェーンの詳細については、「挿入、更新、および削除 に関連するイベントの確認」チュートリアルを参照してください。

GridView で [削除] ボタンをクリックすると、SqlDataSource s Delete() メソッドが呼び出されます

図 5: GridView の [削除] ボタンをクリックすると、SqlDataSource の Delete() メソッドが呼び出される

手順 2: 、、および DELETE ステートメントをINSERTUPDATE自動的に生成する

手順 1 で調べたように、INSERTUPDATEおよび DELETE SQL ステートメントは、プロパティ ウィンドウまたはコントロールの宣言構文を使用して指定できます。 ただし、この方法では、手動で SQL ステートメントを手動で記述する必要があります。これは単調でエラーが発生しやすい可能性があります。 幸い、データ ソースの構成ウィザードでは、ビューのテーブルから列を指定する画面を INSERT使用するときに、、 UPDATE、および DELETE ステートメントを自動的に生成するオプションが提供されます。

この自動生成オプションを調べてみましょう。 のDesignerInsertUpdateDelete.aspxに DetailsView を追加し、そのプロパティを IDManageProducts設定します。 次に、DetailsView のスマート タグから、新しいデータ ソースを作成し、 という名前 ManageProductsDataSourceの SqlDataSource を作成することを選択します。

ManageProductsDataSource という名前の新しい SqlDataSource を作成する

図 6: 名前付きの ManageProductsDataSource 新しい SqlDataSource を作成する (フルサイズの画像を表示する場合はクリックします)

[データ ソースの構成] ウィザードで、接続文字列をNORTHWINDConnectionString使用し、[次へ] をクリックします。 [ステートメントの選択の構成] 画面で、[テーブルまたはビューから列を指定する] ラジオ ボタンを選択したまま、ドロップダウン リストからテーブルを選択 Products します。 チェック ボックスの ProductID一覧から 、 ProductNameUnitPrice、および Discontinued 列を選択します。

Products テーブルを使用して、ProductID、ProductName、UnitPrice、および廃止された列を返します

図 7: テーブルをProducts使用して、、および Discontinued 列をUnitPriceProductIDProductName返します (フルサイズの画像を表示するには、 をクリックします)

選択したテーブルと列に基づいて、、、および DELETE ステートメントを自動的に生成INSERTするには、[詳細設定] ボタンをクリックし、[Generate , , and DELETE statements]\(ステートメントの生成INSERT\UPDATE) チェック ボックスをチェックします。 UPDATE

[INSERT、UPDATE、および DELETE ステートメントの生成] チェックボックスをオンにします

図 8: [Generate , , and statements]\(、、および ステートメントの生成\UPDATE) INSERTチェックボックスをDELETEオンにする

[Generate , , UPDATEand DELETE statements]\(ステートメントの生成INSERT\) チェック ボックスは、選択したテーブルに主キーがあり、主キー列 (または列) が返される列の一覧に含まれている場合にのみ確認できます。 [Generate , , and statements]\(ステートメントの生成INSERT\UPDATEDELETE) チェック ボックスがオンになると、オプティミスティック コンカレンシーを使用するチェック ボックスがオンになると、結果UPDATEの ステートメントと DELETE ステートメントの句が拡張WHEREされ、オプティミスティック コンカレンシー制御が提供されます。 ここでは、このチェック ボックスをオフのままにします。次のチュートリアルでは、SqlDataSource コントロールを使用したオプティミスティック コンカレンシーについて調べます。

[Generate , , and DELETE statements]\(ステートメントの生成INSERT\UPDATE) チェック ボックスをオンにした後、[OK] をクリックして [ステートメントの構成] 画面に戻り、[次へ]、[完了] の順にクリックして、データ ソースの構成ウィザードを完了します。 ウィザードが完了すると、Visual Studio によって、および 列の DetailsView ProductIDProductNameに BoundFields が追加されUnitPrice、列の CheckBoxField がDiscontinued追加されます。 DetailsView のスマート タグから、[ページングを有効にする] オプションをチェックして、このページにアクセスするユーザーが製品をステップ実行できるようにします。 また、DetailsView とプロパティもWidthHeightクリアします。

スマート タグには、[挿入を有効にする]、[編集を有効にする]、[削除を有効にする] の各オプションがあります。 これは、次の宣言構文に示すように、SqlDataSource には、InsertCommandUpdateCommandおよび DeleteCommandの値が含まれているためです。

<asp:DetailsView ID="ManageProducts" runat="server" AllowPaging="True"
    AutoGenerateRows="False" DataKeyNames="ProductID"
    DataSourceID="ManageProductsDataSource" EnableViewState="False">
    <Fields>
        <asp:BoundField DataField="ProductID" HeaderText="ProductID"
            InsertVisible="False" ReadOnly="True" SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" HeaderText="ProductName"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
            SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Fields>
</asp:DetailsView>
<asp:SqlDataSource ID="ManageProductsDataSource" runat="server"
    ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
    DeleteCommand=
        "DELETE FROM [Products] WHERE [ProductID] = @ProductID"
    InsertCommand=
        "INSERT INTO [Products] ([ProductName], [UnitPrice], [Discontinued])
         VALUES (@ProductName, @UnitPrice, @Discontinued)"
    SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
         FROM [Products]"
    UpdateCommand=
        "UPDATE [Products] SET [ProductName] = @ProductName,
         [UnitPrice] = @UnitPrice, [Discontinued] = @Discontinued
         WHERE [ProductID] = @ProductID">
    <DeleteParameters>
        <asp:Parameter Name="ProductID" Type="Int32" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="ProductID" Type="Int32" />
    </UpdateParameters>
    <InsertParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
    </InsertParameters>
</asp:SqlDataSource>

SqlDataSource コントロールの 、、および DeleteCommand プロパティに値InsertCommandUpdateCommandが自動的に設定されていることに注意してください。 プロパティと プロパティでInsertCommand参照される列のセットは、 ステートメントの列にSELECTUpdateCommandづいています。 つまり、 と UpdateCommandすべての Products 列InsertCommandを含めるのではなく、 にSelectCommand指定された列のみが存在します (列ProductIDであるため省略されIDENTITY、編集時に値を変更できず、挿入時に自動的に割り当てられます)。 さらに、および プロパティのInsertCommandUpdateCommand各パラメーターには、および DeleteCommandDeleteParameters コレクションにInsertParametersUpdateParameters対応するパラメーターがあります。

DetailsView のデータ変更機能を有効にするには、スマート タグの [挿入を有効にする]、[編集を有効にする]、[削除を有効にする] の各オプションをチェックします。 これにより、および ShowDeleteButton プロパティが ShowInsertButtonShowEditButtonに設定された CommandField がTrue追加されます。

ブラウザーでページにアクセスし、DetailsView に含まれている [編集]、[削除]、および [新規] ボタンをメモします。 [編集] ボタンをクリックすると、DetailsView が編集モードになり、プロパティが (既定値) にFalse設定されている各 BoundField ReadOnly が TextBox として表示され、CheckBoxField がチェック ボックスとして表示されます。

DetailsView の既定の編集インターフェイス

図 9: DetailsView の既定の編集インターフェイス (フルサイズの画像を表示する場合をクリック)

同様に、現在選択されている製品を削除することも、新しい製品をシステムに追加することもできます。 ステートメントはInsertCommandUnitPriceおよび Discontinued の各列でのみ機能ProductNameするため、他の列にはNULL、挿入時にデータベースによってまたはその既定値が割り当てられます。 ObjectDataSource と同様に、 が を許可NULLせず、既定値を持たないデータベース テーブル列がない場合InsertCommand、ステートメントを実行INSERTしようとすると SQL エラーが発生します。

注意

DetailsView の挿入および編集インターフェイスには、カスタマイズや検証の並べ替えがありません。 検証コントロールを追加したり、インターフェイスをカスタマイズしたりするには、BoundFields を TemplateFields に変換する必要があります。 詳細については、 編集および挿入インターフェイスへの検証コントロールの追加 および データ変更インターフェイスのカスタマイズ に関するチュートリアルを参照してください。

また、更新と削除の場合、DetailsView では現在の製品の DataKey 値が使用されます。これは、 プロパティが構成されている場合 DataKeyNames にのみ存在します。 編集または削除しても効果がない場合は、 プロパティが DataKeyNames 設定されていることを確認します。

SQL ステートメントの自動生成に関する制限事項

[ステートメントDELETEUPDATEの生成INSERT] オプションはテーブルから列を選択する場合にのみ使用できるため、より複雑なクエリの場合は、手順 1 で行ったように独自INSERTの 、UPDATE、および DELETE ステートメントを記述する必要があります。 一般に、SQL SELECT ステートメントでは、 を使用JOINして、表示目的で 1 つ以上の参照テーブルからデータを取り戻します (製品情報を表示するときにテーブルのCategoryNameフィールドを取り戻Categoriesすなど)。 同時に、ユーザーがコア テーブル (Productsこの場合) にデータを編集、更新、または挿入できるようにする必要がある場合があります。

UPDATE、および DELETE ステートメントはINSERT手動で入力できますが、次の時間節約のヒントを検討してください。 最初に SqlDataSource をセットアップして、テーブルから Products データをプルします。 データ ソースの構成ウィザードの [テーブルまたはビューの列の指定] 画面を使用して、および DELETE ステートメントをINSERTUPDATE自動的に生成できるようにします。 次に、ウィザードが完了したら、プロパティ ウィンドウから SelectQuery を構成することを選択します (または、データ ソースの構成ウィザードに戻りますが、[カスタム SQL ステートメントまたはストアド プロシージャの指定] オプションを使用します)。 次に、 ステートメントを SELECT 更新して構文を JOIN 含めます。 この手法は、自動的に生成される SQL ステートメントの時間を節約できる利点を提供し、よりカスタマイズされたステートメントを SELECT 可能にします。

、、および DELETE ステートメントを自動的に生成するINSERTもう 1 つの制限事項は、 ステートメントと UPDATE ステートメントのINSERT列が、 ステートメントによってSELECT返される列に基づいている点です。 UPDATE ただし、フィールドの更新または挿入が必要になる場合があります。 たとえば、手順 2 の例では、BoundField を UnitPrice 読み取り専用にしたい場合があります。 その場合は、 には UpdateCommand表示されません。 または、GridView に表示されないテーブル フィールドの値を設定することもできます。 たとえば、新しいレコードを追加するときに、値を QuantityPerUnit TODO に設定する必要がある場合があります。

このようなカスタマイズが必要な場合は、プロパティ ウィンドウ、ウィザードの [カスタム SQL ステートメントまたはストアド プロシージャの指定] オプション、または宣言構文を使用して手動で行う必要があります。

注意

データ Web コントロールに対応するフィールドがないパラメーターを追加する場合は、これらのパラメーター値に何らかの方法で値を割り当てる必要があることに注意してください。 これらの値は、 または UpdateCommandInsertCommand直接ハードコーディングできます。事前に定義されたソース (クエリ文字列、セッション状態、ページ上の Web コントロールなど) から取得できます。または、前のチュートリアルで説明したように、プログラムで割り当てることができます。

まとめ

データ Web コントロールが組み込みの挿入、編集、削除機能を利用するには、バインドされているデータ ソース コントロールでこのような機能を提供する必要があります。 SqlDataSource の場合は、および DELETE の各 SQL ステートメントを 、UpdateCommand、および DeleteCommand の各プロパティにInsertCommand割り当てる必要があることを意味INSERTUPDATEします。 これらのプロパティと対応するパラメーター コレクションは、手動で追加することも、データ ソースの構成ウィザードを使用して自動的に生成することもできます。 このチュートリアルでは、両方の手法を調べました。

オプティミスティック コンカレンシーの実装に関するチュートリアルで、ObjectDataSource で オプティミスティック コンカレンシー を使用する方法について説明しました。 SqlDataSource コントロールは、オプティミスティック コンカレンシーのサポートも提供します。 手順 2 で説明したように、および DELETE ステートメントをINSERTUPDATE自動的に生成すると、ウィザードには [オプティミスティック コンカレンシーを使用する] オプションが用意されています。 次のチュートリアルで説明するように、SqlDataSource でオプティミスティック コンカレンシーを使用すると、 ステートメントと DELETE ステートメントのUPDATE句が変更WHEREされ、データが最後にページに表示されてから他の列の値が変更されていないことが確認されます。

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。