WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

for...in ループと hasOwnProperty

by WebSurfer 2017年6月30日 15:38

JavaScript の for...in ループ内で hasOwnProperty を使っているコード例を見かけますが、hasOwnProperty を使うことにどういう意味があるかを書きます。(詳細は後述しますが、下の画像で赤枠が hasOwnProperty を使わない場合、青枠が使った場合の結果です)

hasOwnProperty 有無の違い

MDN (Mozilla Developer Network) の記事「Object.prototype.hasOwnProperty()」に "in 演算子と違って、このメソッドはオブジェクトのプロトタイプチェーンをたどってチェックしません" と書いてあって、for...in ループの中で hasOwnProperty プロパティを使うコード例もありましたが、自分的にはどうもピンときません。

なので、自分的に納得できる具体例を以下に書いておきます。

マイクロソフト公式解説書「プログラミング Microsoft ASP.NET MVC」の 10.1.2 章に書いてあったことですが、JavaScript でオブジェクト指向を実装するのにプロトタイプを利用できるそうです。だからプロトタイプを使用したコーディングが主流になっているということなのでしょうか?

プロトタイプを使用したコード例として、本に書いてあったように、Person クラスを以下のように定義したとします。

// Person クラスの疑似コンストラクター
var Person = function (name, lastname, birthdate) {
    this.initialize(name, lastname, birthdate);
}

// メンバー
Person.prototype.initialize = 
                 function (name, lastname, birthdate) {
    this.Name = name;
    this.LastName = lastname;
    this.BirthDate = birthdate;
}
        
// メンバー
Person.prototype.getAge = function () {
    return 18;
}

Person クラスの Own プロパティは Name, LastName, BirthDate のみです。しかしながら、以下のコードのように for...in で Person クラスの各要素をコンソールに出力すると、Name, LastName, BirthDate の他に、プロトタイプチェーンをたどって initialize, getAge も出力されてしまいます。上の画像の赤枠で示した部分が下記のコードの実行結果です。

var p = new Person("web", "surfer", "2017/6/30");

for (var key in p) {
    console.log(key + ':' + p[key]);
}

それを、以下のように、p.hasOwnProperty(key) が true になる要素のみをコンソールに出力すれば、上の画像の青枠部分に示した通り、Name, LastName, BirthDate のみとなります。

for (var key in p) {
    if (p.hasOwnProperty(key)) {
        console.log(key + ':' + p[key]);
    }
}

上記は、あくまで自分が思いついた自分的に納得できる例で、他にもっと適切で分かりやすい例があるかもしれませんが・・・

Tags: ,

JavaScript

NULL 条件でのレコード抽出

by WebSurfer 2017年6月18日 18:02

Visual Studio のデータソース構成ウィザードを使って、@IT の記事「Microsoft Visual Studio 2005 による Web アプリケーション構築技法」の「D. テーブルアダプタへのクエリ追加」のセクションに書いてあるように、特定の条件で DataSet / DataTable を生成するメソッドを作ることができます。

以下の画像はサンプルデータベース Northwind の Orders テーブルから ShippedDate が NULL のレコードを抽出して DataGridView に表示したものですが、このようなことができるメソッドを作るにはどうすればよいかということを書きます。

ShippedDate が NULL のレコードを抽出

SELECT クエリで WHERE ShippedDate = @ShippedDate という条件で TableAdapter にメソッドを追加すると、そのメソッドの引数に null が渡された場合は、メソッド内部でパラメータ @ShippedDate に DBNull.Value が代入されるコードが生成されます。

しかしながら、それでは ShippedDate が NULL のレコードを抽出することはできません。ShippedDate が NULL のレコードを抽出するためには WHERE ShippedDate IS NULL とする必要があります。

ではどうすればいいかですが、クエリビルダで元となる SELECT クエリを作成する際、@IsNull_ShippedDate(名前は任意)というような引数に null が渡されたか否かを判定するパラメータを追加し、以下の画像のように WHERE 句を組み立ててそれをベースにメソッドを生成します。

クエリビルダ

これにより、DataTable を返す GetDataByNullableShippedDate メソッド(名前は任意)の例ですが、以下のようなコードが生成されます。(注:自動生成されたコードそのものではなく、読みやすくするため改行等を行っています)

public virtual OrdersDataTable GetDataByNullableShippedDate(
               int? IsNull_ShippedDate, DateTime? ShippedDate)
{
  this.Adapter.SelectCommand = this.CommandCollection[1];
  if (IsNull_ShippedDate.HasValue == true) 
  {
    this.Adapter.SelectCommand.Parameters[0].Value = 
                                (int)IsNull_ShippedDate.Value;
  }
  else
  {
    this.Adapter.SelectCommand.Parameters[0].Value = 
                                                DBNull.Value;
  }

  if (ShippedDate.HasValue == true)
  {
    this.Adapter.SelectCommand.Parameters[1].Value = 
                                 (DateTime)ShippedDate.Value;
  }
  else
  {
    this.Adapter.SelectCommand.Parameters[1].Value = 
                                                DBNull.Value;
  }
  OrdersDataTable dataTable = new OrdersDataTable();
  this.Adapter.Fill(dataTable);
  return dataTable;
}

この GetDataByNullableShippedDate メソッドを以下のように呼び出して DataTable を取得し DataGridView に表示したのが一番上の画像です。

public partial class Form14 : Form
{
  private OrdersTableAdapter adapter = new OrdersTableAdapter();
  private BindingSource bindingSource1 = new BindingSource();
  private NorthwindDataSet.OrdersDataTable table;
        
  public Form14()
  {
    InitializeComponent();

    DateTime? shippedDate = null;

    table = adapter.GetDataByNullableShippedDate(
                shippedDate.HasValue ? 0 : 1, shippedDate);

    bindingSource1.DataSource = table;
    this.dataGridView1.DataSource = bindingSource1;
  }
}    

もちろん、NULL でない特定の日付の ShippedDate のレコードも上のコードで抽出できます。

Tags: ,

ADO.NET

ASP.NET Core で文字化け

by WebSurfer 2017年6月16日 20:37

ASP.NET Core のプロジェクトで、スキャフォールディング機能を利用して生成した View に日本語を書き込むと、文字化けするという話を書きます。元の話は Teratail の記事「ASP.NET Core でスキャフォールディングすると文字化けする」です。

ASP.NET Core での文字化け

上の画像で赤枠で囲った部分が問題の文字化けです。どのような理由でそうなるかを以下に説明します。

(注:何行にもわたって書き込むとサーバーエラーになり、"An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately. "�" is not valid at the start of a code block. Only identifiers, keywords, comments, "(" and "{" are valid." というエラーメッセージが出ることもあります)

検証に使った環境は以下の通りです。Visual Studio 2017 でも同じ問題が出るとのことです。

  • Visual Studio 2015 Community 2015 Update 3
  • ASP.NET Core Web Application (.NET Core) のテンプレート使用
  • Windows 10 Professional 64-bit
  • IIS 10.0 Express

そもそもの原因は、スキャフォールディング機能を利用して生成した View の文字コードが Shift_JIS になってしまうことです(日本語 OS の場合)。.NET Framework ベースの MVC プロジェクトの View はデフォルトでは BOM 付きの UTF-8 になります。それを Shift_JIS に変換しても文字化けの問題は出ません。何故か、Core では Shift_JIS では対応できず、上の画像のように文字化けします。

Fiddler を使って応答をキャプチャし、問題の文字 "�" のバイト列を調べてみたら EF BF BD となっていました。

MSDN ライブラリ「.NET Framework における文字エンコーディング」によると "Unicode デコーダーでは、デコードできない 2 バイトのシーケンスが REPLACEMENT_CHARACTER (U+FFFD) に置き換えられます" とのことです。U+FFFD は UTF-8 のバイト列では EF BF BD となります。

ということは、Shift_JIS の日本語の文字列は(多分、他の非 ASCII 文字も)、ASP.NET Core ではその文字列を処理する際デコードできず、REPLACEMENT_CHARACTER に置き換えられ、その UTF-8 のバイト列 EF BF BD がサーバーから送られてきたということのようです。

何故 Core ベースのアプリでは View の文字コードが Shift_JIS になってしまうのか、何故 Shift_JIS ではデコードできないのかは不明ですが、解決するには View の文字コードを UTF-8 にするだけで OK です。

ただし、Visual Studio のオプション設定などで自動的に UTF-8 で保存する方が見つかりません。なので、プリミティブな方法ですが、メモ帳で当該 View を開いて UTF-8 で保存し直すことでとりあえず解決しました。

最後に、上の画像を表示した View をどのように作ったかを書いておきます。

まず、以下のような Controller と Model を作ります。(Controller と Model が一緒になっているのは単に分けるのが面倒だったからです)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace AspNetCore.Controllers
{
  public class SampleModel
  {
    public string FirstName { set; get; }
    public string LastName { set; get; }
  }

  public class HomeController : Controller
  {
    public IActionResult Sample()
    {
      ViewData["SubTitle"] = "日本語";

      var model = new List<SampleModel>
      {
        new SampleModel() {FirstName="山田",LastName="太郎"},
        new SampleModel() {FirstName="日本",LastName="花子"}
      };

      return View(model);
    }
  }
}

上のコードにある SampleModel をベースにスキャフォールディング機能を利用して View を自動生成します。具体的には、以下の画像のように、Visual Studio のソリューションエクスプローラで、View のフォルダを右クリック ⇒[追加(D)]⇒[ビュー(V)...]で「ビューの追加」ダイアログを開き、必要な情報を入力して[追加]ボタンをクリックします。

「ビューの追加」ダイアログ

生成される View のコードは以下のようになります。文字コードは Shift_JIS になりますが、この時点では日本語が含まれていませんので問題は出ないです。

@model IEnumerable<AspNetCore.Controllers.SampleModel>

@{
    ViewData["Title"] = "Sample";
}

<h2>Sample</h2>

<p>
  <a asp-action="Create">Create New</a>
</p>
<table class="table">
  <thead>
    <tr>
      <th>
        @Html.DisplayNameFor(model => model.FirstName)
      </th>
      <th>
         @Html.DisplayNameFor(model => model.LastName)
      </th>
      <th></th>
    </tr>
  </thead>
  <tbody>
@foreach (var item in Model) {
    <tr>
      <td>
        @Html.DisplayFor(modelItem => item.FirstName)
      </td>
      <td>
        @Html.DisplayFor(modelItem => item.LastName)
      </td>
            
      ・・・中略・・・

    </tr>
}
  </tbody>
</table>

Visual Studio のエディタから上のコードの一部を、以下のように日本語にします。

@model IEnumerable<AspNetCore.Controllers.SampleModel>

@{
    ViewData["Title"] = "日本語";
}

<h2>@ViewData["SubTitle"]</h2>

<p>日本語</p>

<p>
  <a asp-action="Create">Create New</a>
</p>

・・・以下略・・・

これを Visual Studio のバイナリエディタで開いたのが以下の画像です。"日本語" の文字列が 93 FA 96 7B 8C EA と Shift_JIS のバイト列になっているのが分かるでしょうか。

バイナリエディタで表示

この状態の View を表示させたのが一番上の画像です。

文字化けしているのは ViewData["Title"] = "日本語"; として _Layout.cshtml に渡し、それから title タグに設定した文字列(一番上の画像で上の赤枠)と、<p>日本語</p> とした部分(下の赤枠)です。

Controller から ViewData["SubTitle"] = "日本語"; として View に渡した文字や、Model から DisplayFor 経由で View に渡した文字は問題ありません。

一般的にまだ枯れてないフレームワークにはいろいろ不具合があると思いますが、今回の問題もそういった不具合の一つということでしょうか。

ちなみに、ビューのフォルダを右クリック⇒[追加(D)]をクリック ⇒[新しい項目(W)...]をクリック ⇒[MVC ビューページ]を選択して名前を付けて[追加(A)]をクリック・・・で生成される空の View ファイルは UTF-8 になります。なので、やはり本来は UTF-8 になるべきなのであろうと思います。

ググって調べてもこの問題は日本語の記事には見つからなかったのですが、英語の stackoverflow には同様な問題の報告がありました。

Special characters in Razor template not being encoded correctly

Asp net core MVC View creating view default encoding VS 2017

前者の記事のコメントに known issue だとして Encoding in scaffolded Items へのリンクが張ってあって、その記事によると "closed this on May 6 2016" となっていますが、直ってません。どういうことかは不明です。

Tags: , ,

MVC

About this blog

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

Calendar

<<  2018年1月  >>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar