WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

Visual Studio 2019 がフリーズする

by WebSurfer 21. June 2020 13:30

2020/7/10 追記: Developer Community フォーラムによると、この記事の問題は v16.6.3 で fix されているそうです。自分も v16.6.3 にバージョンアップ後は問題ないことを確認しました。

自分が使っている Visual Studio Cummunity 2019 のバージョンを 16.6.0 にアップグレードした後、[ヘルプ(H)]⇒[Microsoft Visual Studio のバージョン情報(A)]でバージョン情報を表示しようとすると、

ヘルプでバージョン情報を表示

以下の画像で示したようにビジー状態となってしまいます。一旦こうなってしまうと Visual Studio の操作が不能になってしまい、タスクマネージャーで Visual Studio を終了する他手がなくなってしまいます。

VS2019 がビジー状態

4 月に MySQL 8.0.19 をインストールしたのですが、その時同時にインストールされた MySQL for Visual Studio v1.2.9 との相性の問題が原因のようで、それを Visual Studio の[拡張機能(X)]⇒[拡張機能の管理()]で表示される「拡張機能の管理」ダイアログで無効にすると、

拡張機能の管理

以下の画像の通りバージョン情報は表示されるようになりました。(MySQL の何をインストールしたかの詳細は、先の記事「MySQL をインストールしました(その 3)」を参照ください)

バージョン情報

調べてみると、Microsoft の Developer Community フォーラム About Microsoft Visual Studio frozen で報告が上がっていて、MySQL for Visual Studio 以外にも Oracle Developer Tools for Visual Studio 2019 Version: 19.3.1.0 で同様な問題が出るそうです。

というわけで、どうしても Oracle の開発ツールを使う必要がある時以外は無効にしておいた方が良いかもしれません。自分が使ってきた限りではバージョン情報の表示以外の不具合は気が付かなかったのですが・・・

Developer Community フォーラムによると "A fix for this issue has been internally implemented and is being prepared for release." とのことですので、そのうち修正されることを期待してます。

Tags: , , ,

DevelopmentTools

Dispose パターン (その 2)

by WebSurfer 20. June 2020 13:32

先の記事「Dispose パターン」で、Visual Studio による Dispose パターンの自動生成機能の説明と、その機能を利用して自作カスタムクラスに Dispose パターンを実装する例を書きました。

この記事では Dispose パターンを実装済のクラスを継承してカスタムクラスを作成する際に、それに Dispose が必要な .NET のクラスのインスタンスを保持する場合、どのようなコードを書けばよいかについて述べます。

説明に ASP.NET MVC のプロジェクトを作成するテンプレートで ASP.NET Identity を実装した時に生成される AccountController の例を挙げます。自分が書いたコードで説明するより説得力があると思いますので。(笑)

Visual Studio が自動生成する Controllers/AccountController.cs のコードは以下のようになっています。この記事の説明に不要な部分は省略しています。

namespace Mvc5App2.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        private ApplicationSignInManager _signInManager;
        private ApplicationUserManager _userManager;

        // ・・・中略・・・

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_userManager != null)
                {
                    _userManager.Dispose();
                    _userManager = null;
                }

                if (_signInManager != null)
                {
                    _signInManager.Dispose();
                    _signInManager = null;
                }
            }

            base.Dispose(disposing);
        }

        // ・・・中略・・・
    }
}

AccountController の継承元 Controller クラスは IDisposable インターフェイスを継承しており Dispose パターンを実装しています。

フィールド _signInManager と _userManager には、ApplicationSignInManager と ApplicationUserManager が初期化されてそれらのオブジェクトへの参照が代入されるようにコーディングされています。

ApplicationSignInManager と ApplicationUserManager は継承元が IDisposable インターフェイスを継承しており Dispose パターンを実装しています。

AccountController には上に紹介した「Dispose パターン」の記事のような Dispose パターンのコードは実装できませんが、AccountController が Dispose されるときには _signInManager と _userManager も Dispose する必要があります。

というわけで、上のコードのように、Controller クラスが実装している Dispose(bool) メソッドをオーバーライドして _signInManager と _userManager が Dispose されるようにしています。

ASP.NET が要求の処理を終えてコントローラーをアンロードする際、自動的にコントローラーの Dispose() メソッドが実行されます。デバッガで上の Dispose(bool) メソッド内にブレークポイントを置いて実行し、AccountController のアクションメソッドを呼び出してみてください。Dispose(bool) メソッドに制御が飛んでくることで Dispose されるのが分かると思います。

(自分が試した限りですが、上の Dispose(bool) メソッドに制御が飛んでくる前のどこかで _signInManager と _userManager は Dispose されるようで、デバッガで値を調べると null になっていました。それゆえ null をチェックするコードが入っているようです)

次に、Windows Forms アプリの例を紹介します。下のコードは Visual Studio で自動生成されたフォームの .Designer.cs のコードです。(この記事の説明に不要な部分は省略しています)

namespace WindowsFormsApplication1
{
    partial class Form3
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.bindingSource1 = new BindingSource(this.components);
            this.bindingNavigator1 = new BindingNavigator(this.components);
            // ・・・中略・・・
        }

        // ・・・中略・・・
}

上のコードで Form3 は Form クラスを継承しており、Form クラスは Dispose パターンを実装しています。なので、上の AccountController の場合と同様に、Form クラスが実装している Dispose(bool) メソッドをオーバーライドし、内部で使用された Dispose が必要な .NET のクラスを Dispose するようになっています。

AccountController の場合とは違って .NET のクラスへの参照を直接 Dispose するのではなく、Container クラス(コンポーネントをカプセル化し追跡するコンテナ)にまとめて Dispose しています。

デザイナ画面で Dispose が必要なコントロール�� Form にドラッグ&ドロップすると、.Designer.cs の InitializeComponent メソッド内に Container クラスを初期化し、そのコントロールを Container オブジェクトに追加するコードが自動生成されます。上のコード例では、BindingSource と BindingNavigator が Container オブジェクトに追加されています。

そのあたりの詳しい説明は @IT の記事「第4回 Visual Studio 2010のひな型コードを理解する (3/4)」が参考になると思います。

なお、デザイナ画面を使わないで自分でコードを書いた場合は、InitializeComponent メソッド内には上のようなコードは自動生成されませんので、.cs ファイルの方に自力でコードを書くことになります。具体例は別の記事「XML ファイルを DataGridView に表示」のコードを見てください。

また、IDisposable を継承しているクラスでも、全てが自動的に Container オブジェクトに追加されることはないです。例えば DataGridView クラスとか DataSet クラスがそうです。ホントに Dispose が必要かという疑問はありますが、IDisposable を継承しているクラスは使い終わったら Dispose するのが基本のようですので、上に紹介した記事のように自分でコードを書いて Container オブジェクトに追加した方がよさそうです。(初期化する前に Add しても無効のようですので注意してください)

Container オブジェクトに登録しておけば、フォームの右上の × 印アイコンをクリックするなどしてフォームを閉じる際に、自動的にフォームの Dispose() メソッドが実行され、上のコードの Dispose(bool) メソッドも実行されます。(これもブレークポイントを置いて実行してみれば、そこに制御が飛んでくることで Dispose されるのが分かると思います)

Tags: ,

.NET Framework

Web API と要求ヘッダの application/xml

by WebSurfer 7. June 2020 14:12

.NET Framework 版の ASP.NET Web API を要求する際、要求ヘッダの Accept に application/xml が含まれると応答は JSON ではなく XML になります。

ASP.NET Web API の応答

知ってました? 実は自分は知らなかったです。(汗) ちなみに、MVC の Json メソッド、および Core 3.1 Web API と MVC の Json メソッドは、要求ヘッダの Accept に application/xml が含まれていても JSON が返ってきます。

Web API の応答をチェックするには Postman などのツールを使うのが一般的なようです。でも、GET 要求ならブラウザのアドレスバーに直接 URL を入力して応答を見ることでも十分だったので自分は IE11 を使ってそうしてました。それで期待した通り JSON 文字列が返ってきてました。

ところが、Chrome, Edge, Firefox では応答が JSON ではなく XML になってしまいます。理由は要求ヘッダの Accept に application/xml が含まれるからのようです。ハマったのは上の画像のようなサーバーエラーとなってしまったことです(HTTP 500 応答が返ってきます。上の画像は形式は XML ですが中身はエラーメッセージです)。

エラーメッセージ "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'." によるとシリアライズに失敗したということです。

IE11 や jQuery ajax を使った検証ではシリアライズには問題はないことは確認しており、ブラウザに Chrome などを使ったからと言ってそれがシリアライズの部分に影響があるとは考えられなかったです。何故なのでしょう?

循環参照の問題でした。

上の画像の InnerException のエラーメッセージにある Blog_E36679EB... D5AD0 という文字列が、先の記事「JSON シリアライズの際の循環参照エラー」にもあったのを思い出して気が付きました。

.NET Framework 版の ASP.NET Web API の JSON シリアライザは Newtonsoft の Json.NET のもので、JsonIgnoreAttribute クラスという属性が利用できます。なので、問題のプロパティに [JsonIgnore] を付与してシリアライズの際の循環参照エラーは回避していました。

でも [JsonIgnore] が有効なのは JSON にシリアライズするときのみで、XML にシリアライズするときは循環参照の問題は回避できません。

Chrome, Edge などを使った時は、ASP.NET Web API は要求ヘッダの Accept に application/xml が含まれるのを見て、XML にシリアライズしようとして循環参照の問題に陥ったということのようです。

Tags: , , ,

Web API

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  July 2020  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar