WebSurfer's Home

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

ブラウザ定義ファイル

by WebSurfer 2014年3月4日 13:32

ASP.NET には「ブラウザ定義ファイル」というものがあります。(下の画像は ASP.NET 4 用のブラウザ定義ファイルです。これとは別に ASP.NET 2.0, 3.0, 3.5 用のブラウザ定義ファイルが v2.0.50727 サブフォルダ下にあります)

ASP.NET のブラウザ判定はこれらのファイルに書かれた定義が基になるはずなのですが、実際の判定結果はファイルの内容と違うことがある・・・即ち、HttpBrowserCapabilities オブジェクトを調べないと分からない・・・という話を書きます。

ブラウザ定義ファイル

ASP.NET は、実行時に、ブラウザから送られてくる User Agent と、.browser ファイルに書かれている定義を基にブラウザの機能を判定し、ブラウザに送信する html, Javascript などの内容を調整します。詳しくは MSDN ライブラリの ブラウザー定義ファイルのスキーマ (browsers 要素) を見てください。

IE10 がリリースされた直後に、IE10 で ASP.NET ページにアクセスすると __doPostBack() などが定義されてないというスクリプトエラーが出たということがありました。この原因は、IE10 のリリース直後にはまだ ASP.NET に IE10 のブラウザ定義がなかったからです。その後の Windows Update で IE10 のブラウザ定義が追加され、問題が解消されたという経緯があります。

先日 IE11 がリリースされ、その User Agent が大幅に変更された("MSIE" という文字列が含まれなくなりました。詳しくは MSDN ライブラリの User-agent string changes を参照ください)ということで、それに伴いブラウザ定義ファイルがどう変わったかを調べてみました。

ところが、ASP.NET 2.0, 3.0, 3.5 用のブラウザ定義ファイルの内容と、ASP.NET のブラウザ判定結果(HttpRequest.Browser プロパティで取得できる HttpBrowserCapabilities オブジェクト)が、IE11 はおろか IE10 でも一致しません。(ASP.NET 4 の場合は一致しました。)

例えば、ASP.NET 3.5 のサイトで、HttpBrowserCapabilities.Version プロパティで正しく IE10, IE11 のバージョン(それぞれ 10.0, 11.0)が取得できましたが、ブラウザ定義ファイルにはどこにもそのような定義は見当たりません。

おかげで理由を調べるのに半日ぐらいハマってしまいました。(汗)

そんな時に見つけたのが MSDN Blog の記事 ASP.NET の IE10 対応について です。

詳しくはその記事を読んでいただきたいのですが、要するに、ASP.NET は「ブラウザ定義ファイル」使っておらず、System.Web.dll の中に持っているブラウザ判定ルーチンを使っているのだそうです。

また、Windows Update による更新では Sysytem.Web.dll を更新して既定のルーチンは置き換えるものの、必ずしもブラウザ定義ファイルは更新されないようです。

なんのこっちゃ・・・という感じです。(笑)

Tags:

ASP.NET

Windows Forms アプリで Membership を利用

by WebSurfer 2014年2月11日 17:47

ASP.NET の Membership クラスRoles クラス を Windows Forms アプリケーションでも利用できるという話です。

Windows Forms アプリで Membership と Roles を利用

Membership オブジェクトおよび Roles オブジェクトは、ASP.NET が web.config の情報を基に Membership プロバイダおよび Role プロバイダ(ユーザー情報のストアが SQL Server の場合は SqlMembershipProvider クラス および SqlRoleProvider クラス)を利用して自動的に生成します。

プログラマがコードを書いて Membership オブジェクト、Roles オブジェクトを作る手段は提供されていません。なので、ASP.NET Web アプリケーションでしか使用できないと思っていましたが、実はそうではなかったです。(汗)

そのことは、MSDN フォーラムで Using the ASP.Net membership provider in a Windows forms application part 1. という記事を紹介されて、初めて知りました。

その記事によると、Windows コンソールアプリでも(ASP.NET でなくても)、app.config から(web.config からでなくても)設定値を取得して、Membership オブジェクトや Roles オブジェクトを自動的に生成してくれるということでした。

自分でも、以下のコードの Windows Forms アプリを作って試してみましたが、確かにその通りであることが確認できました。上の画像が以下のコードを実行したときのものです。

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Web.Security;

namespace ASPNETMembershipWindowsForms
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
            
      this.listBox2.DataSource = Roles.GetAllRoles();

      List<string> userNames = new List<string>();
      foreach (MembershipUser user in Membership.GetAllUsers())
      {
        userNames.Add(user.UserName);
      }
      this.listBox1.DataSource = userNames;
    }

    private void button1_Click(object sender, EventArgs e)
    {
      if (Membership.ValidateUser(this.textBox1.Text, 
            this.textBox2.Text))
      {
        this.label3.Text = "Result: User validated.";
      }
      else
      {
        this.label3.Text = "Result: User invalid!";
      }            
    }

    private void button2_Click(object sender, EventArgs e)
    {
      if (this.listBox1.SelectedItem == null)
      {
        this.label6.Text = "Please select a user.";
        return;
      }

      if (this.listBox2.SelectedItem == null)
      {
        this.label6.Text = "Please select a role.";
        return;
      }

      string user = this.listBox1.SelectedItem.ToString();
      string role = this.listBox2.SelectedItem.ToString();

      try
      {
        if (Roles.IsUserInRole(user, role))
        {
          this.label6.Text = "Yes";
        }
        else
        {
          this.label6.Text = "No";
        }
      }
      catch (Exception ex)
      {
        this.label6.Text = ex.Message;
      }
    }
  }
}

app.config

<?xml version="1.0"?>
<configuration>
  <configSections>
  </configSections>
  <connectionStrings>
    <add name="MemberShip" 
      connectionString="..." 
      providerName="System.Data.SqlClient"/>
  </connectionStrings>

  <system.web>
    <machineKey validationKey="..." 
      decryptionKey="..." 
      validation="SHA1" 
      decryption="AES"/>

    <membership defaultProvider="SqlProvider">
      <providers>
        <remove name="SqlProvider"/>
        <add name="SqlProvider" 
          type="System.Web.Security.SqlMembershipProvider" 
          connectionStringName="MemberShip" 
          applicationName="MyHomePage" 
          enablePasswordRetrieval="false" 
          enablePasswordReset="true" 
          requiresQuestionAndAnswer="true" 
          requiresUniqueEmail="false" 
          passwordFormat="Hashed" 
          maxInvalidPasswordAttempts="5" 
          minRequiredPasswordLength="7" 
          minRequiredNonalphanumericCharacters="0" 
          passwordAttemptWindow="10"/>
      </providers>
    </membership>
        
    <roleManager enabled="true" defaultProvider="RoleProvider">
      <providers>
        <clear/>
        <add name="RoleProvider" 
          type="System.Web.Security.SqlRoleProvider" 
          connectionStringName="MemberShip" 
          applicationName="MyHomePage"/>
      </providers>
    </roleManager>
  </system.web>
  <startup>
    <supportedRuntime version="v2.0.50727"/>
  </startup>
</configuration>

今回の検証では、既存の ASP.NET Web アプリケーションの Forms 認証用の ASPNETDB.mdf を利用ました。

なので、上記 app.config で既存の ASPNETDB.mdf に接続できるように接続文字列を設定し、machineKey 要素、membership 要素、roleManager 要素の設定は既存の Web アプリの web.config と同じにしています。

machineKey 要素の設定まで同じにしたのは、MSDN ライブラリの How To: ASP.NET 2.0 で MachineKey を構成する方法 の「メンバシップ」のセクションに "暗号化パスワード フォーマットを選択すると、データの暗号化と暗号化解除のときに <machineKey> 設定が使用されます" という記述があったからです。

ただし、これは passwordFormat が "Encrypted" に設定されている場合のようで、デフォルトの "Hashed" の場合は関係なさそうです。

それから、ランタイムバージョンが .NET 2.0 になっているのは、.NET 4 では何故か MembershipUser が使えないからです。

オブジェクトブラウザを見ても MembershipUser 以外にも .NET 4 に含まれないクラスが多々あります。理由不明ですが、今日はもうこれ以上追求する気力がないです。(笑)

----- 2014/2/13 追記 -----

.NET 3.5 以前では System.Web.dll のみを参照設定に追加すれば使えた MembershipUser その他のクラスが .NET 4 では使えない理由が分かりました。

Microsoft の ASP.NET のサイトの ASP.NET 4 Breaking Changes という記事の MembershipUser and Other Types in the System.Web.Security Namespace Have Been Moved という記事に以下のように書いてありました。

"Some types that are used in ASP.NET membership have been moved from System.Web.dll to the new System.Web.ApplicationServices.dll assembly. The types were moved in order to resolve architectural layering dependencies between types in the client and in extended .NET Framework SKUs."

従って、.NET 4 以降では System.Web.ApplicationServices.dll も参照設定に追加しないと MembershipUser クラスは使えません。

ただし、"System.Web.ApplicationServices.dll was added to the list of referenced assemblies that is used by default by the ASP.NET compilation system." とのことなので、ASP.NET Web アプリケーションではプログラマが参照設定を追加する必要はないそうです。

詳しくは上に紹介した Microsoft のサイトの記事を見てください。

Tags: ,

Authentication

DataGridView に ID と名前を併記

by WebSurfer 2014年2月1日 15:48

先の記事 DataGridView に ComboBox を表示 では CategoryID, SupplierID(数字)に代えて、名前を Categories, Suppliers テーブルから取得して ComboBox に表示する方法を書きました。

ComboBox など使わないで、ID と名前の両方を並べて表示すればよいというケースもあると思います。ここでは、Northwind サンプルデータベースの Products テーブルと Categories テーブルを例に使い、下の画像のように CategoryID と CategoryName を並べて表示する方法を書きます。なお、Visual Studio の TableAdpter 構成ウィザードを利用してアプリを作るのが前提です。

DataGridView に ID と名前を表示

たぶん最初に考え付くのは、TableAdpter 構成ウィザードで、以下のように INNER JOIN した SELECT クエリをベースに型付 DataSet + TableAdapter を作ることではないでしょうか?

SELECT Products.ProductID, Products.ProductName, 
       Products.CategoryID, Categories.CategoryName,
       Products.UnitPrice, Products.Discontinued
FROM   Products INNER JOIN Categories 
ON     Products.CategoryID = Categories.CategoryID

しかしながら、テーブルを JOIN した SELECT クエリをベースに TableAdapter を自動生成させると UPDATE, DELETE, INSERT に必要なコードは一切自動生成されません。

表示するだけではなく、Products テーブルは更新する必要があるという場合、以下のように TableAdpter 構成ウィザードのクエリビルダで、単一の Products テーブルを対象とする SELECT クエリをベースに TableAdapter を生成すれば、Products テーブルを更新するのに必要なメソッドを自動生成してくれます。

クエリビルダ

上の画像の SELECT クエリは以下の通りです。CategoryName を抽出する部分はクエリビルダは自動的には作ってくれませんので、自力で書く必要があります。

SELECT ProductID, ProductName, CategoryID,
       (SELECT CategoryName FROM Categories
        WHERE (Products.CategoryID = CategoryID))
       AS CategoryName,
       UnitPrice, Discontinued
FROM   Products

型付 DataSet + TableAdapter が完成したら、データソースウィンドウを開くとその中に Products という項目があるはずなので、それを Form にドラッグ&ドロップするだけで、DataGridView 上で Products テーブルの表示、編集、更新ができるアプリが完成するはずです。

DataDridView の CategoryName 列の dataGridViewTextBoxColumn は Visual Studio が自動的に ReadOnly = true に設定して生成してくれます(ユーザー入力はできないようになります)。

なお、上記は Products テーブルのみ更新をできればよいというケースですので注意してください。Products テーブルと Categories テーブルの両方を同時に更新したい場合は TableAdapterManager で紹介したように階層更新が可能な形にする必要があります。

Tags: ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar