EntityFramework6を使って、SQL Server Compact4とSQLiteでコードファーストをしてみた

EntityFrameworkを使ったコードファーストに興味があったため、

という、手軽に扱えるデータベースで使う場合について調査した時のメモです。

なお、SQL Server Compactについては、すでにdeprecatedとなっています。
Is SQL Server Compact discontinued from Visual Studio 2013? - Stack Overflow

また、以下のコードをほぼそのまま使ってコードファーストをやってみました。
EF 4.1の目玉機能「コード・ファースト」を理解しよう - @IT

 

環境

 

SQL Server Compactの場合

上記の@ITの記事よりも更に導入が簡単になっていました。

  • NuGetからEntityFramework.SqlServerCompact(作成者:Microsoft)をインストール
  • ターゲットプラットフォームを x86 に変更 すれば、App.configを修正することなく、そのまま行うことができました。

データについては\bin\Debug\EntityCompact.ItemCatalog.sdfに保存されています。VisualStudioなどで内容を確認すると、テーブルが作成されていてデータも登録されていました。

 

SQLiteの場合

残念ながら、現時点のEntityFrameworkではSQLiteに対してDDL実行ができないようです。ただ、テーブル構造さえできていれば、データを登録することなどができました。

なお、Web上には EntityFramwork + SQLiteの記事がいくつかありましたが、バージョンによって設定する内容が異なるようで、今回は自分の環境で動作したものを記載しておきます。

 

NuGetにてインストール

以下をインストールすることで、EntityFrameworkも合わせてインストールされます。

 

App.config の修正

SQL Server Compactとは異なり、App.configがそのままでは動作しないため、いくつか手を入れます。

なお、以下の方法は自分の環境でトライ&エラーした結果なので、より良い方法があるのかもしれません。

 

DbProviderFactories要素の変更

NuGetからインストールした場合、App.configにいろいろと追記されますが、EF6 + SQLite 1.0.93.0ではDBに接続することができません。

そのため、invariant属性がSystem.Data.SQLite.EF6、type属性がSystem.Data.SQLite.SQLiteFactory, System.Data.SQLiteと、自動的に生成されたものをミックスしたような内容を指定します。

<DbProviderFactories>
  <remove invariant="System.Data.SQLite.EF6" />
  <add name="SQLite Data Provider" invariant="System.Data.SQLite.EF6" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>

 

connectionStrings要素の追加

connectionStrings要素がないと、defaultConnectionFactory要素で指定しているFactoryを使用してしまうため、connectionStringsを設定します。

なお、ポイントとしては、DbContextと同じ名称を name に設定しておくことと、SQLiteの外部キー制約を有効にするためのパラメータをセットすることになります。

<connectionStrings>
  <add name ="ItemCatalog" connectionString="Data Source=|DataDirectory|sample.sqlite;foreign keys=true;" providerName="System.Data.SQLite.EF6"/>
</connectionStrings>

SQLiteの外部キー制約についての参考

 
ちなみに、既存のdefaultConnectionFactory要素のかわりに、System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6を記載したところ以下のエラーが発生したため、defaultConnectionFactory要素はそのままにしてconnectionStrings要素を追加する形を取りました。

System.InvalidOperationException

Failed to set Database.DefaultConnectionFactory to an instance of the 'System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6' type as specified in the application configuration.See inner exception for details.

InnerException

型 'System.Data.SQLite.EF6.SQLiteProviderFactory' のオブジェクトを型 'System.Data.Entity.Infrastructure.IDbConnectionFactory' にキャストできません。

 

警告の解決

VisualStudioの状態によっては、以下の警告が出るかもしれません。

要素'entityFramework'には無効な子要素'providers'が含まれています。必要とされる要素は'contexts'です。

 
そのため、以下の記事を参考に Entity Framework 6 Tools for Visual Studio 2012 & 2013 をインストールすれば、警告はなくなります。
entity framework - How to resolve Warning : The element 'entityFramework' has invalid child element 'providers'. List of possible elements expected: 'contexts' - Stack Overflow

 

プログラムの修正

@ITのままでは動作しないため、プログラムを修正します。

 

DDLの追加

上記の通り、今のところはEF6 + SQLiteではDDL実行ができないようなので、別途DDL実行をするメソッドを追加して呼ぶようにします。

なお、SQL Server Compactの動きと同じような感じになるよう、

  • プライマリーキーをオートインクリメント
  • 外部キー制約を設定
  • テーブル名は複数形で指定(MemberではなくMembers)
  • SQLiteのファイル(sample.sqlite)は、実行時のフォルダへ出力

と設定します。

static void ExecuteDDL()
{
    var path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "sample.sqlite");
    System.Data.SQLite.SQLiteConnection.CreateFile(path);

    var cnStr = new System.Data.SQLite.SQLiteConnectionStringBuilder() { DataSource = path };

    using (var cn = new System.Data.SQLite.SQLiteConnection(cnStr.ToString()))
    {
        cn.Open();

        var sql = "CREATE TABLE Members (Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Address TEXT, TelNo TEXT); ";
        sql += "CREATE TABLE Items (Id INTEGER PRIMARY KEY AUTOINCREMENT, Price INTEGER, MemberId INTEGER, Name TEXT, SoldAt datetime, FOREIGN KEY(MemberId) REFERENCES Members(Id))";

        var cmd = new System.Data.SQLite.SQLiteCommand(sql, cn);
        cmd.ExecuteNonQuery();

        cn.Close();
    }
}

 
参考:
c# - Create SQLite Database and table - Stack Overflow

 

コードファーストで使うクラスのプロパティの型を変更

SQLiteの仕様として、INTEGER型のプライマリーキーは64bitになることからC#でプライマリーキーをint型で指定すると、以下のエラーが発生します。

The type of the key field 'Id' is expected to be 'System.Int32', but the value provided is actually of type 'System.Int64'.

そのため、プライマリーキーと外部キー制約でプライマリーキーを参照しているプロパティはlong型にします。

public class Item
{
    public long Id { get; set; }
    public int Price { get; set; }
    public long MemberId { get; set; }
    public string Name { get; set; }
    public Nullable<System.DateTime> SoldAt { get; set; }
    public virtual Member Member { get; set; }
}

参考:

 

 
以上の対応で、SQL Server Compactと同じコードで動作しました。

また、データベースの中身はPupSQLiteなどで確認できました。
Pup's Atelier-Software

 

感想

SQL Server Compactについては、deprecatedにさえなっていなければ、自分の用途ではこれで十分だったのかもしれないと感じました。

一方、SQLiteについてはDDL実行できないのがツライです。

そのため、お手軽に使えるデータベースにおいて、EntittyFrameworkによるコードファーストは今のところなかなか難しいと思いました。

 

ソースコード

GitHubに上げておきました。
CSharp-Sample/EntityFramework6/SQLiteSQLServerCompact4 at master · thinkAmi/CSharp-Sample

1つのソリューションに2つプロジェクトを入れてありますので、スタートアッププロジェクトに設定でそれぞれを切り替えることができます。
方法 : スタートアップ プロジェクトを設定する - MSDN

 

参考

Google I/O 2014 Extended in 信州に参加してきました #gdgshinshu

6/25の深夜に開催されたGoogle I/O 2014 Extended in 信州に参加してきました。

 
去年初めて信州会場で参加して楽しかったことと、同時通訳のおかげで理解しやすかったこと、近くで開催してくれるのはありがたいことから、今年は早々に参加を申し込みました。

結果としては、昨年同様に同時通訳が分かりやすかったり、信州会場の他の方のコメントを聞けたり、一人で見るよりも眠くならなかったりと、今年も参加してよかったです。

また、Google I/O 2014 Extended の日本仕様のTシャツやGoogle I/O 2014 Extendedのステッカーを会場にていただきました。着ていると一体感があっていいですね。

 
内容はすでにほかのサイトの方が詳しく出ているので省略しますが、自分は「Native Office Editing」が気になったので、今後使ってみようかと思います。
[速報]Google Appsで、Word/Excel/PowerPointファイルを変換せずそのまま編集、保存可能に。Google Driveは容量無制限へ。Google I/O 2014 - Publickey

また、GDG信州のサイトを見ると今年もDevFest Japan 2014 I/O 報告会が予定されていました。今のところ日付は未定なものの*1、開催されれば参加してみたいと思います。
GDG Shinshu

 
最後になりましたが、信州会場の主催をしてくださったGDG Shinshu Organizerの@さん、信州会場で参加されたみなさま、ありがとうございました。

*1:GDG京都のページによると、7/19でしょうか - DevFest Japan 2014 Summer in 京都 - Google Developer Group 京都

Android Wear Hackathon in Shinshu に行ってきました #gdgshinshu

6/21(土)、Android Wear Hackathon in Shinshu に行ってきました。
Android Wear Hackathon in Shinshu on Zusaar

場所は、塩尻市民交流センター えんぱーくでした。
ホームページ|えんぱーく

Togetterは以下にまとめられています。ありがとうございます。
Android Wear Hackathon in Shinshu - Togetterまとめ

Android Wearについてあまり事前学習をしないままの参加となりましたが、いろいろと詳しい説明があり、理解を深めることができました。

 

内容

午前

@ さんと @ さんによるコードラボでした。

開発環境構築からHello Worldまでを、EclipseAndroid Studioの両方での説明があり、分かりやすかったです。資料のリンクは上のZussarに記載されています。

以下は自分のTwitterメモです。

  • JDKは7がいいらしい
  • Android Studio 0.6系はまだまだこれかららしい*1
  • エミュレータは、丸だと角が見えなくなることから、四角のほうがいいとのこと
  • Android Wearの配布されているのは、今のところAndroid Studio向けのものらしい
  • おすすめのサンプルは WearLabelNotificationsSmaple
  • Projectを作るときには EmptyActivityを選ぶと、フラグメントが存在しないものができあがる

 

午後

お昼をはさんで、午後はもくもくとハッカソンを行い、最後にアイデアや実装結果の発表がありました。

自分は何の成果もありませんでしたが、他の方の発表を聞いていろいろなアイデアがあるのだなぁと感心していました。

あとは早くPreviewが取れるのを期待したいところです。

 

サイン本

@ さんよりサイン入りの著書「データビジュアライゼーションのためのD3.js徹底入門」をいただきました。ありがとうございます。

以前の勉強会でD3.jsの紹介があった時は面白そうだけれど難しそうと感じましたが、これを機会にさわってみようかと思います。

 

自分がやっていたこと

AndroidStudioでハッカソンを進めようとしましたが、XamarinでAndroid Wearのアプリを作成している記事を見つけたので、それならRubotoでできないかを試すことにしました。
XamarinでAndroid Wearサンプルを動かしてみる - Qiita

動くところまではたどり着くことができませんでしたが、AndroidやRubotoまわりで色々と知ることができたので、よかったということにしたいです。事前の準備が足りていませんでした...

会場で調べてたりしたことは以下の通りです。

 

Rubotoで実機とエミュレータを同時起動している時の、実機へのアプリインストール

rake installとしてもerror: more than one device and emulatorというエラーが出ます。そのままの状態ではエミュレータを落とした後に実機へインストールし、再度エミュレータを起動する必要があります。手間ですね。

原因は、rakelib/ruboto.rake にて adb install -r "#{APK_FILE}" 2>&1 などと、対象を指定しないまま実行しているためです。
ruboto/assets/rakelib/ruboto.rake - ruboto/ruboto

そこで、adbコマンドを使用しているところ全てに、-dオプションを追加すれば、エミュレータを落とさずに実機へのインストールが可能になりました。
エミュレータと実機を同時に接続した状態で、ADBで簡単に実機にソフトをインストールする方法 | Taosoftware

 

wearable-preview-support.jarへの参照の追加

Rubotoで外部ライブラリを追加する方法は、以下のチュートリアルにありました。
Tutorial: barcode scanning · ruboto/ruboto Wiki

ただ、上記の例と異なり、wearable-preview-support.jarにはプロジェクトがなかったため、実際に追加するところを調べていたら時間が切れました。

 

今後のイベント

直近では、6/25の深夜に、Google I/O 2014 Extended in 信州 が開催されます。
Google I/O Extended in 信州 on Zusaar

また、Google I/O を受けての DevFest Japan 2014 I/O 報告会も予定されているようです。詳しくはGDG信州のサイトにて発表があるかと思います。
GDG Shinshu

 
 
最後になりましたが、開催していただいた運営者のみなさま、ありがとうございました。

*1:Twitterでも情報を頂きました。ありがとうございます。 https://twitter.com/masanobuimai/status/480168290900119552

C# + MS Access にて、DapperとTableAdapterで INSERT時のオートナンバー型の値の取得方法を比べてみる

他のデータベースにもあるような、INSERT時に自動的に連番を入れてくれる型として、MS Accessではオートナンバー型が用意されています。

オートナンバー型の自動採番された値をINSERT時に取得する方法について、型付Datase(TableAdapter)とDapperではどのように違うのかを調べてみました。

 

TableAdapterの場合

TableAdapterの場合、hogeTableAdapter.Update() メソッドとTableAdapterの RowUpdated イベントを使います。 詳細は以下が参考になります。

 
ただ、Dapperに比べて作業量が多いこともあり、作業のメモを残しておきます。

 

TableAdapterのパーシャルクラスを生成

Datasetのデザインビューを開き、TableAdapterの上で右クリック、コードの表示を選びます。

 

RowUpdatedイベント用のメソッドを作成し、 @@IDENTITY の値を取得するロジックを追加
private void Adapter_RowUpdated(object sender, System.Data.OleDb.OleDbRowUpdatedEventArgs e)
{
    if (e.StatementType == System.Data.StatementType.Insert
        && e.Status == System.Data.UpdateStatus.Continue)
    {
        var cmd = new System.Data.OleDb.OleDbCommand("SELECT @@IDENTITY", null);
        cmd.Connection = e.Command.Connection;
        cmd.Transaction = e.Command.Transaction;

        var result = cmd.ExecuteScalar();

        if (result != null
            && result.GetType() != typeof(System.DBNull))
        {
            e.Row["ID"] = (int)result;
            e.Row.AcceptChanges();
        }
    }
}

 

RowUpdatedのイベントハンドラを追加するメソッドを用意

C#のTableAdapterでは以下の制限があります。

 
そこで、RowUpdatedのイベントハンドラを追加するメソッドを用意し、実行時に忘れないようにそのメソッドを呼び出ます。

public void AddRowUpdatedEvent()
{
    this.Adapter.RowUpdated += new System.Data.OleDb.OleDbRowUpdatedEventHandler(Adapter_RowUpdated);
}

 
なお、手元の環境のせいかもしれませんが、this.Adapter のかわりにWebでよく見かける this._adapterを使うと、実行時にNullReferenceExceptionが発生しました。

 

実際の使用方法

イベントハンドラを追加するメソッドを呼んでから、Update()メソッドを呼びます。

//  TableAdapterのRowUpdatedイベントハンドラの追加
ta.AddRowUpdatedEvent();

var ds = new SampleDataset();
var row = ds.Item.NewItemRow();
row.ItemName = "秋映";
ds.Item.AddItemRow(row);

ta.Update(ds);

 

TableAdapterの接続情報について

TableAdapterは自前の接続情報を持っているため、通常は動的に切り替えることができません。

ただ、今回はTableAdapterの接続文字列を動的に切り替えることが必要になったので、一番簡単そうなPropertiesの設定のコード表示からパーシャルクラスを作成する方法を選びました。
参考: Ito Blog » Blog Archive » TableAdapter の接続先をコードから設定

なお、上記以外の方法はあまりお手軽でない気がしたので、今回は使いませんでした。

 

Dapperの場合

TableAdapterではいろいろと面倒でしたが、Dapperでは簡単に取得することができました。

参考にしたコードはSQL Server Compact Edition向けのコードでしたが、MS Access向けでも問題なく動作しました。
Dapper Getting Started - lancscoder / Connection.cs

var sql = "INSERT INTO Item(ItemName) VALUES (@ItemName)";
cn.Execute(sql, new { ItemName = "秋映" });

var id = (int)cn.Query("SELECT @@IDENTITY as ID").First().ID;

 
なお、上記の「@@IDENTITY クライシスを管理する」にも記載がある通り、MS Accessでは単一バッチで複数SQLステートメントを実行できないため、stackoverflowの回答にあるようなことをやろうとしてもエラーになります。
c# - How do I perform an insert and return inserted identity with Dapper? - Stack Overflow

 

OLE DB接続について

話は変わりますが、過去にOLE DB接続が廃止されるということを目にしましたが、よく調べてみるとそれはSQL Serverだけのようでした。

今のところは公式でも、

This deprecation applies to the Microsoft SQL Server OLE DB provider only. Other OLE DB providers as well as the OLE DB standard will continue to be supported until explicitly announced.

と記載されているため、とりあえずは使っていけそうです。

 

ソースコード

GitHubに上げておきました。前回のものに追加してあります。
CSharp-Sample/DapperApp at master - thinkAmi/CSharp-Sample

 

参考

C# + MS Accessで、OLE DB接続にてDapperを使う

C# + MS Access 環境で型付Datasetを使っていたのですが、Datasetのデザイナなどがツライです。

そこで、DapperをOLE DB接続で試してみたところ動作したため、いろいろと試してみました*1
StackExchange/dapper-dot-net

なお、Dapperの基本的な使い方については、以下のBlogとスライドが参考になりました。
ORMとかdapper dot netについてお話させていただきました - きよくらの備忘録

 

環境

 

準備

スライドにもある通り、NuGetでプロジェクトに追加して、コードの中で using Dapper; するだけです。

あとは、ビルドのプラットフォームターゲットを x86 にすれば、MS Accessの32bit版でも問題なく動作するかと思います。

 

MS Accessで使ってみる

ほぼスライドのままですが...。

ここで紹介している以外のサンプルコードとしては、DapperのTestsフォルダの中にある Tests.cs が参考になります。
dapper-dot-net/Tests/Tests.cs at master · StackExchange/dapper-dot-net · GitHub

MS Accessだからといって、SQL Serverのサンプルとかと書き方は変わりません。

 

接続

System.Data.OleDb.OleDbConnection に接続文字列を渡し、中で Open() Close() すればOKです。

接続文字列はSettings.settingsに設定したものを使うか、そのままの文字列を渡します。

using (var cn = new System.Data.OleDb.OleDbConnection(new Connection().ConnectionString))
{
    cn.Open()
    // hoge
    cn.Close()
}

public class Connection
{
    public string ConnectionString { get; private set; }
    public Connection()
    {
        //  接続文字列はSettings.settingsに設定したものを使える(Properties.Settings.Default)が、今回は自前で文字列を渡してみた
        var path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "sample.accdb");
        ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path;
    }
}

 

SELECT
インテリセンスがきかない書き方

インテリセンスはきかないですが、ささっと書いて使うにはこれで十分な気がしました。

const string SQLSelect = @"SELECT * FROM Item WHERE ItemName = @ItemName";
const string SelectParameter = "りんご";

var results = cn.Query(SQLSelect, new { ItemName = SelectParameter });

 

インテリセンスがきく書き方

型を用意しておけば、インテリセンスがきくようになります。

const string SQLSelect = @"SELECT * FROM Item WHERE ItemName = @ItemName";
const string SelectParameter = "りんご";

//  この第二引数にはインテリセンスはきかない
var results = cn.Query<Item>(SQLSelect, new { ItemName = SelectParameter });

//  ここでインテリセンスがきく
var msgs = results.Select(a => a.ID + a.ItemName);
Console.WriteLine(string.Join(Environment.NewLine, msgs));


//  用意した型
public class Item
{
    public int ID { get; set; }
    public string ItemName { get; set; }
}

 

INSERT
var sql = @"INSERT INTO Item(ItemName) VALUES(@ItemName)";
var count = cn.Execute(sql, new { ItemName = DateTime.Now.Millisecond.ToString() });

 

UPDATE
var sql = @"UPDATE Item SET ItemName = 'シナノゴールド' WHERE ID = @Id";
var count = cn.Execute(sql, new { Id = 1 });

 

トランザクション

ExecuteメソッドtransactionパラメーターSystem.Data.IDbTransactionオブジェクト を渡せばトランザクションになります。

using (var tr = cn.BeginTransaction())
{
    var insert = @"INSERT INTO Item(ItemName) VALUES(@ItemName)";
    cn.Execute(insert, new { ItemName = DateTime.Now.Millisecond.ToString() }, transaction: tr);

    var update = @"UPDATE Item SET ItemName = 'シナノゴールド' WHERE ID = @Id";
    cn.Execute(update, new { Id = 1 }, transaction: tr);

    tr.Commit();
}

 

コード

GitHubに上げておきました。
CSharp-Sample/DapperApp at master · thinkAmi/CSharp-Sample

 

その他の参考

*1:Entity Frameworkは動かなそうなので、やめておきました - c# 4.0 - How to use Entity framework for MS Access database - Stack Overflow

JAWS-UG 長野支部キックオフミーティング at 第52回NSEG勉強会 に行ってきました #nseg #glnagano #jawsug

6/14(土)、JAWS-UG 長野支部キックオフミーティング at 第52回NSEG勉強会 に行ってきました。
第52回勉強会 - 長野ソフトウェア技術者グループ NSEG

場所はギークラボ長野でした。
GEEKLAB. NAGANO

Togetterは以下にまとめられています。ありがとうございます。
JAWS-UG長野支部キックオフ(NSEG 第52回勉強会) - Togetterまとめ

今回の勉強会では、AWSのどのサービスを使って構築したかという事例がいくつも紹介されていて、実際に構築する時に参考になりそうでした。

2014/6/17 追記

@さんと@さんのプレゼンを追加

2014/6/17 追記 ここまで

 

勉強会

ギークラボ長野について (@ さん)

プレゼン:長野で一番熱いスペースのはなし

ギークラボ長野の施設や開催しているイベントの紹介がありました。

平日の勉強会や土曜日の朝のもくもく会など、勉強会向けの場所提供だけではなくいろいろと精力的にイベントが開催されており、こういう場所が身近にあるのはうらやましくなりました。

 

クラウド概論 (@ さん)

クラウドの概要が分かりました。ZaaSだけは使いたくないです...。

 

AWSクラウドで構築するスマホアプリバックエンド (@ さん)

プレゼン:AWS クラウドで構築するスマホアプリ バックエンド

AWSや各種サービスを使った実際の構成例やその時のTipsなどが挙げられていました。今後構築する際、とても参考になりそうです。

プレゼンの中で紹介されていたモノもいくつか気になり、こちらも触れていきたいと思いました。

 

AWS Essentials (@ さん)

プレゼン:AWS Essentials

AWSの主なサービスと、AWS関係のイベント、おすすめのAWSのサービスの紹介がありました。

個人的にはAmazon WorkSpacesが気になりました。オンプレミスのActiveDirectoryと連携できるとのことで、早く東京リージョンに来るといいなと感じました。

あと、プレゼンの時に使っていたガジェットが気になりましたが、コクヨ石と教わりました。

 

Rubyaws-sdkAWS がサイコーにクールな 3 つの理由 (山口 与力 さん)

プレゼン:JAWS-UG Nagano Kickoff Meeting // Speaker Deck

IAM Roleprykumogata の3つに関する紹介でした。

特に、pryについてはirbと比較して紹介されていたので差がとても分かりやすく、今後はpryを使っていこうと思いました。

 

AWS導入事例 (@ さん)

各社でのAWS導入事例が色々と紹介されていました。

印象に残ったのはNHK紅白歌合戦の事例で、東京リージョンだけではダメで全リージョンを使って800~1,000台構築したというのには驚きました。一方で、一つのリージョン内での限界をどう見極めたのかは気になりました。

また、クラスメソッドさんのBlogは内部的にも業務日報のような感じで使ってるとのことでした。たしかにWeb上にいろいろとあればいろいろと振り返りやすいと感じました。

すでに今回の勉強会のBlogエントリも上がっていました。
【JAWS】いま長野がアツい!JAWS-UG長野キックオフに参加してきました | Developers.IO

 

懇親会

同じ会場にてビアバッシュ形式で開かれた懇親会では、ギークラボ長野が誕生した経緯や今後のイベント、他のギークハウスやWindows関係の話も聞けて楽しめました。

 

ギークラボ長野について

初めて行ったものの、道路沿いに2枚の掲示板、壁面にビルの名前(県都ビル)があり、ちょうどよい目印になりました。

ギークラボ長野の設備として、WiFiが利用可能だったり超短焦点プロジェクターが準備されており、IT系の勉強会を開催したりするのに良さそうな環境でした。また、技術評論社・日経系*1の雑誌や蔵書もあり、もくもくしやすそうでした。

 

 
最後になりましたが、開催していただいた運営者のみなさま、ありがとうございました。

*1:日経ネットワークがあったのには驚いた

C# + ClickOnceで、WPFアプリを配信するときに設定することなど

久しぶりにClickOnceC#アプリを作成・配信してみたらやり方を忘れていた部分があったため、メモを残しておきます。

環境

 

アプリのアイコンの生成

自分でicoファイルを用意するか、VisualStudioでアイコンを作成します。VisualStudioで生成する手順は以下となります。

  • プロジェクト > 新しい項目の追加 > アイコンファイル でアイコンファイルを生成
  • プロジェクトのProperties > アプリケーションタブ > 生成したicoファイルを指定

参考:アプリケーションに独自のアイコンを設定するには? - @IT

 

発行バージョンの取得

ApplicationDeployment.CurrentDeployment.CurrentVersion を使って取得します。
ただ、ClickOnceで配布・起動された時のみ値を取得でき、それ以外の場合は空白となります。

参考:ApplicationDeployment.CurrentVersion プロパティ (System.Deployment.Application)

 

ClickOnceでのファイルの配布

アプリで使う画像ファイル

WPFの場合、リソースファイルは使わずにXAMLで対象の画像ファイルを指定します。

また、ファイルのプロパティにて、ビルドアクションにコンテンツ を設定しておきます。

参考:XAMLにてImageコントロール内に画像を表示する - プログラミング備忘録

 

テキストファイルをデータファイルとして配布する

プロジェクトのPropertiesへの設定として以下を行います。

なお、データファイルとして配布する場合、アプリをアンインストールするとファイルが削除されてしまうことに注意します。

  • すべてのファイルを表示して、対象のテキストファイルをプロジェクトに含める
  • ビルドアクションを コンテンツ にする
  • 発行タブのアプリケーションボタンで、該当のファイルの発行の状況を データファイル にする

また、データファイルのディレクトリは以下の方法で取得できます。

System.Deployment.Application.ApplicationDeployment.CurrentDeployment.DataDirectory.ToString()

 
参考:

 

テキストファイルをClickOnceのキャッシュ領域へ配布する

キャッシュ領域の場合はアンインストールしてもファイルは残る一方で、ClickOnce発行ごとにファイルが配信されます。

  • すべてのファイルを表示して、対象のテキストファイルをプロジェクトに含める
  • ビルドアクションを コンテンツ にする
  • 出力ディレクトリにコピーを 常にコピーする にする

また、キャッシュ領域のディレクトリは以下の方法で取得できます。

System.AppDomain.CurrentDomain.BaseDirectory

 
参考:

 

コード署名の追加

コード署名を追加しないでClickOnceで配布した際、プログラムのインストールや更新時の発行者欄に「不明な発行元です」というメッセージが表示されます。

そのため、Active Directory環境下でAD CSが構築済の場合、コード証明書を発行し署名を追加します。

コード証明書については、AD CS にてコード証明書を発行し、MMCの証明書でユーザーアカウントの個人に発行した証明書を追加します。

参考:AD CS でコード証明書を発行する - スクリプトセンター

なお、既存の証明書テンプレートコード証明書(有効期間1年)を使って発行しても良いのですが、複製して期間を延長したものを使用しても特に問題はありませんでした。

証明書を追加後、プロジェクトのプロパティ「署名」にて、以下の設定を行います(なお、今回はアセンブリへの署名は行いません)。

  • ClickOnceマニフェストに署名する:チェックを入れる
  • ストアから選択ボタンより、上記で作成した証明書を選択

設定後にClickOnceでアプリを配信すると、発行者欄に証明書が表示されるようになります。

 

プロジェクトのPropertiesの発行タブの設定

発行タブの設定により、ClickOnceを発行する共有フォルダの場所や、プログラム起動前の更新チェックなどを指定できます。

参考:連載 ClickOnceの真実:第3回 Visual Studio 2005でClickOnceを極めよう (2/2) - @IT

今回は、ClickOnceアプリを共有フォルダで配信する、クローズな社内LANで利用することを想定して設定してみました。

タブ 種類 項目 内容
発行 発行場所 発行フォルダーの場所 <ファイルサーバー名>\共有フォルダ\
インストールモードと設定 アプリケーションはオフラインでも利用できる ●チェックする
更新ボタン アプリケーションの更新を確認する ■チェックする
アプリケーションの更新を確認する方法 ●アプリケーションの開始前に行う
オプション - 説明 発行者名 <任意の名前>
オプション - 説明 スイート名 <任意の名前>
オプション - 説明 製品名 <任意の名前>
オプション - マニフェスト URLからアプリケーションが起動されるのを禁止する ■チェックする
オプション - マニフェスト デスクトップショートカットの作成 ■チェックする

 
なお、発行者名・スイート名・製品名を入力すると、すべてのプログラムの中に

発行者名(フォルダ)
`-- スイート名(フォルダ)
    `-- 製品名(ショートカットアイコン)

の形でフォルダとショートカットアイコンが作成されます。

 

ClickOnceでの更新の強制

上記のプロジェクトのPropertiesの発行タブの設定により、アプリの更新をほぼ強制することができますが、

  • ユーザーがスキップを選んでも、プログラムの更新を強制する
  • スタートメニューから起動可能にする
  • 「このアプリケーションに最低限必要なバージョンを指定する」のは毎回更新するのが手間なのでやりたくない

ということもあるかもしれないので、コードでアップデートを強制するようにします(なお、System.Windows.Formsの参照設定が必要になります)。

if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed)
{
    var deploy = System.Deployment.Application.ApplicationDeployment.CurrentDeployment;
    if (deploy.CheckForUpdate())
    {
        deploy.Update();

        System.Windows.Forms.Application.Restart();
        System.Windows.Application.Current.Shutdown();
    }
}

 
Windows Formアプリの場合は以下が参考になります。

 
また、自社環境以外でClickOnceを使うには、以下も参考になるかもしれません。
開発者以外が配置する ClickOnce アプリケーションの作成

 

サンプルコード

CSharp-Sample/ClickOnceApp at master - thinkAmi/CSharp-Sample

なお、サンプルコードにはコード署名を付けていません。

 

その他参考