SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

UWPアプリ開発の最前線

UWPアプリからSQL Serverにアクセス
~.NET Standard 2.0の威力! WPFやXamarinからも共通に使えるクラスライブラリを作る

UWPアプリ開発の最前線 第3回


.NET Standard 2.0規格のADO.NETのコーディング

 SQL Serverを使う場合、データベースを読み書きする方法は従来のADO.NET 2.0と同様です。DbConnectionを使ってデータベースに接続したら、DbDataAdapterDataSetでドカっとやり取りするもよし、DbDataReaderで1レコードずつ処理するもよし、です。それらは、.NET Standard 2.0に含まれています。違いは、それらのインスタンスを得るためのDbProviderFactoryの実装が.NET Standardには含まれていない、つまりプラットフォーム依存だということです。

従来のADO.NETとの違い

 SQL Serverを使うためのDbProviderFactoryの実装はSqlClientFactoryクラス(System.Data.SqlClient名前空間)なのですが、それは.NET Standard 2.0規格には含まれていません。そこで、アプリのプラットフォーム依存コード側でSqlClientFactoryのインスタンスを取得して、.NET StandardのPCLへ渡すようにします(次の図)。.NET StandardのPCL側は、それを(SqlClientFactoryの継承元クラスである)DbProviderFactoryとして受け取って、ADO.NETのコードを実行します。ここさえ押さえておけば、あとは従来のADO.NETのスキルでコーディングできるでしょう。

SqlClientFactoryのインスタンスはプラットフォーム依存コード側から渡す
SqlClientFactoryのインスタンスはプラットフォーム依存コード側から渡す

 また、SqlClientFactoryがプラットフォーム依存ということは、そこから生成されるDbConnectionなどの実装もプラットフォーム依存だというになります。プラットフォームごとのテストが必須だということですね。

 なお、UWPではSQL ServerのLocalDBを利用できません(WPFとWindows Formsから利用できることは確認できました。Android/iOS等は未確認)。UWPでローカルにデータベースを置きたいという場合は、SQLite データベースを利用します。

SQL Serverからデータを取得するコード例

 それでは実際のコード例を見ていきましょう。まずは、.NET Standard PCL側で、SQL Serverからデータを取得する方から(⇒Northwindクラス)。

 前述したように、SqlClientFactoryのインスタンスをプラットフォーム側のコードから受け取るようにします。また、SQLユーザー認証しか使えないプラットフォームもあるので、認証方式も切り替えられるようにしましょう。ここでは、どちらもメソッドの引数として受け取ることにしました。SqlClientFactoryインスタンスは引数そのものとして、また、認証方法の切り替えは省略可能なbool値の引数として受け取ります。

 まず、クラスメンバーに接続文字列の定数を2つ定義しておきます(次のコード)。

Northwindクラスに定義する接続文字列の例
// SQL ユーザー認証用の接続文字列
private const string CONN_STR_SQLSERVER
  = @"Data Source=Win10VM-RS3.corp.BluewaterSoft.jp\SQLEXPRESS;"
    + "Initial Catalog=NORTHWIND;User ID=nwtestuser;Password=;Persist Security Info=False;";

// Windows 認証用の接続文字列(UWPアプリ接続用)
private const string CONN_STR_ENTERPRISE
  = @"Data Source=Win10VM-RS3.corp.BluewaterSoft.jp\SQLEXPRESS;"
    + "Initial Catalog=NORTHWIND;Integrated Security=SSPI";

 上はあくまでも接続文字列の例です。試すときは実際のものに適宜書き直してください。なお、AndroidやiOSなどからも利用する場合、接続文字列のホスト名(SQL Serverの名前)はDNSで名前解決できなければいけないようです(WindowsはもちろんNetBIOSでの名前解決でもOK)。また、テスト用ということで、SQLユーザー認証のパスワードを空にしていますが、運用環境では絶対にやってはいけないことなのは言うまでもありません。

 SQL ServerのCategoriesテーブルから全レコードを抽出して返すメソッドは、次のコードのようになります。SqlClientFactoryのインスタンスをDbProviderFactoryクラスとして受け取り(第1引数)、そこからDbConnectionなどのDBアクセスに必要なオブジェクトを取得しています。また、第2引数に応じて接続文字列を切り替えています。

SQL Serverからデータを取得するメソッド
public static DataTable GetCategories(DbProviderFactory factory,
                                      bool useEnterpriseAuthentication = false)
{
  using (DbConnection conn = factory.CreateConnection())
  {
    // 第2引数に応じて接続文字列を切り替える
    conn.ConnectionString = useEnterpriseAuthentication ?
                            CONN_STR_ENTERPRISE : CONN_STR_SQLSERVER;

    using (DbCommand command = factory.CreateCommand())
    {
      command.Connection = conn;
      command.CommandText = "select * from Categories order by CategoryID asc";

      using (DbDataAdapter da = factory.CreateDataAdapter())
      {
        da.SelectCommand = command;

        var dt = new DataTable("Categories");
        da.Fill(dt);
        return dt;
      }
    }
  }
}

データ取得ライブラリを呼び出すコード例

 次に、上のGetCategoriesメソッドを呼び出す側のコードです。プラットフォーム依存コードから呼び出さないといけないので、PCLを使ったXamarin.Formsだけは複雑になります(これはXamarin.Formsの「定石」です)。

Windows Forms/WPF

 従来のWindowsデスクトップアプリから呼び出すには、たった1行ですみます(⇒Form1.csMainWindow.xaml.cs)。SqlClientFactoryクラスのInstance静的フィールドがSqlClientFactoryのインスタンスを返してくれるので、それを引数にして.NET Standard側のメソッドを呼び出すだけです。ただし、.NET Standard 2.0に対応している.NET Framework「本家」はバージョン4.6.1以降(+.NET Core 2 SDK)だということは、忘れないでください。

Windows Forms/WPFからデータを取得するメソッドを呼び出す
DataTable dt = UF03StdLib.Northwind.GetCategories(SqlClientFactory.Instance);

Xamarin.Forms

 Xamarin.Formsで「PCLプロジェクト」を作った場合(前述の「.NET Standard 2.0規格クラスライブラリを使うには?」で説明した[Code Sharing Strategy]セクションで[.NET Standard]を選んだ場合)、UIのコードビハインドは(.NET Standard 2.0の)PCLに置かれます。つまり、コードビハインドやそこから呼び出すコードでは、プラットフォーム依存であるSqlClientFactoryのインスタンスを作れないのです。

 ではどうするかというと、これがXamarin.Formsの「定石」になるのですが、DependencyServiceという一種のDIコンテナを使います。そのコーディング手順は次のようになります。

  1. PCLプロジェクト(UIをコーディングしているプロジェクト)に、インターフェースを定義します
  2. プラットフォームごとのプロジェクトで、インターフェースを実装するクラスを記述します。そのクラスには、Dependency属性(Xamarin.Forms名前空間)を付けておきます
  3. PCLプロジェクト内で、DependencyServiceクラスのGetメソッドを使って、プラットフォームに依存するオブジェクトを取得して使います

 まず、PCLプロジェクトに定義するインターフェースです(⇒ISqlClientFactoryDS.cs)。PCLプロジェクトではSqlClientFactoryクラスを書けませんから、その親クラスであるDbProviderFactory抽象クラスを返すように定義します。

DependencyServiceの定義
public interface ISqlClientFactoryDS
{
  DbProviderFactory Instance { get; }
}

 次に、プラットフォームごとのDependencyServiceの実装です。名前空間の記述が異なる以外は同じなので、ここではAndroid版のみを載せておきます(⇒SqlClientFactoryDS.cs)。実装は、SqlClientFactory.InstanceをDbProviderFactory抽象クラスとして返すだけです。

DependencyServiceの実装(Androidの例)
[assembly: Xamarin.Forms.Dependency(typeof(XamarinFormsSample.Droid.SqlClientFactoryDS))]
namespace XamarinFormsSample.Droid
{
  public class SqlClientFactoryDS : ISqlClientFactoryDS
  {
    public DbProviderFactory Instance
      => System.Data.SqlClient.SqlClientFactory.Instance;
  }
}

 最後に、PCLプロジェクトに戻って、データ取得メソッドを呼び出すコードを書きます(⇒MainPage.xaml.cs)。DependencyServiceを使ってプラットフォーム依存のSqlClientFactoryインスタンスを取得し、それを引数にして.NET Standard 2.0のコードを呼び出しています。

DependencyServiceを使ってデータを取得するメソッドを呼び出す
// DependencyServiceを使ってSqlClientFactoryインスタンスを取得する
var sqlClient = DependencyService.Get<ISqlClientFactoryDS>().Instance;

// .NET Standard 2.0 PCLにあるデータ取得メソッドを呼び出す
DataTable dt = UF03StdLib.Northwind.GetCategories(sqlClient);

UWP

 UWPも、従来のデスクトップアプリと同様に、呼び出すコードは1行だけです(⇒MainPage.xaml.cs)。ここでは第2引数にtrueを渡して、Windows認証を使っています。

UWPからデータを取得するメソッドを呼び出す
DataTable dt = UF03StdLib.Northwind.GetCategories(SqlClientFactory.Instance, true);

 ただしUWPでは、Package.appxmanifestの「機能」(capability)に次に示す宣言を追加する必要があります(Xamarin.FormsのUWPアプリも同じ)。

  • SQLユーザー認証の場合:プライベートネットワーク(privateNetworkClientServer)
  • Windows認証の場合:エンタープライズ認証(enterpriseAuthentication)
    enterpriseAuthenticationは、公式ドキュメント「アプリ機能の宣言」の「特殊な用途および制限された用途に関する機能」セクションに「特殊な用途の機能」として分類されており、通常はストアに提出するアプリでは許可されません。組織内で展開するアプリ専用のcapabilityだと考えてください。

 また、.NET Core 2.0をサポートしているWindows 10はbuild 16299以降だということも忘れないでください。16299未満で止まってしまったWindows 10 Mobile(本稿執筆時点で15254.248)では動作しません。

まとめ

 .NET Standard 2.0規格に含まれるAPIは大幅に増えました。業務ロジックを.NET Standard 2.0のPCLにまとめておけば、それはWindows Forms/WPF/ASP.NETといった従来からの.NET Frameworkのコードから利用できるだけでなく、UWP/Xamarin/ASP.NET Coreといった新しいアプリのコードからも利用できます(Xamarin.AndroidやXamarin.iOSなどからも!)。新しく業務ロジックを開発するときは、.NET Standardで書けないか検討してみてください。

 また、UWPアプリでも、ついにSQL Serverへ直接アクセスできるようになりました。

UWPアプリに興味が湧いてきた人へ

 Windowsデスクトップ用アプリの開発はWindows Formsなら経験があるけど、ちょっとUWPアプリ開発も気になってきたという方へ。ぴったりの電子書籍を書きました!

UWP アプリ開発 101 第2版: Windows Forms 開発者のための C# / XAML による UWP アプリ開発入門』 (BluewaterSoft 2017/7/1)
Kindle版UWPアプリ版があります。
・どちらも無料で最初の方を読めるので、とりあえず試し読みをしてみてください。

修正履歴

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
UWPアプリ開発の最前線連載記事一覧

もっと読む

この記事の著者

biac(ばいあっく)

HONDA R&Dで自動車の設計をやっていた機械屋さんが、技術の進化スピードに魅かれてプログラマーに。以来30年ほど、より良いコードをどうやったら作れるか、模索の人生。わんくま同盟の勉強会(名古屋)で、よく喋ってたりする。2014/10~2019/6 Microsoft MVP (Windows Devel...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/10702 2019/11/10 20:56

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー