fc2ブログ
 

Technology へようこそ
ここは技術者の「経験」と「ノウハウ」のブログです


2010年08月10日

ブラウザ外実行モード(OOB)でのアプリ更新

通常、SilverlightアプリはWebブラウザ上で動作しますので、アプリのバージョンアップは特に意識することなくWebサーバ上に配置したモジュールを入れ替えるだけで事足りるわけですが、ローカルPCにダウンロードしたモジュールを利用する「ブラウザ外実行モード(OOB)」ではどのようにしてバージョンアップすればいいのでしょう?
いたって素朴な疑問ですが、当然ながらそのための仕組みが用意されています。

下記コードをアプリ起動時(Appクラスのコンストラクタ辺りでしょうか)に入れておくだけで、バージョンチェック、および変更されている場合は自動的にダウンロードまで実行してくれます。
チェック+ダウンロードは非同期に行われるため、サンプルでは完了イベントを定義してエラーチェックやメッセージ通知を行っていますが、更新処理そのものは CheckAndDownloadUpdateAsync メソッドを一発呼ぶだけ。

問答無用、確認なしで更新(ダウンロード)されてしまうのはどうかと思いますが、バージョンチェック用のファイルをサーバ上に配置しておくなどすれば、事前にユーザに対して更新通知することもできるでしょうから、更新の仕組み自体はこれで充分かと思われ。
んでも、アセンブリが複数に分割されているような複雑なアプリの場合はどうすればいいんでしょうかね…。
// OOB自動更新
if (App.Current.IsRunningOutOfBrowser)
{
App.Current.CheckAndDownloadUpdateCompleted += (s, arg) =>
{
if (arg.Error == null && arg.UpdateAvailable)
MessageBox.Show("アプリケーションを更新しました。\n" +
"変更を反映するにはアプリケーションを再起動してください。",
"更新通知", MessageBoxButton.OK);
else if (arg.Error != null)
MessageBox.Show("アプリケーション更新でエラーが発生しました。\n" +
"\n[詳細]\n" + arg.Error.Message,
"更新エラー", MessageBoxButton.OK);
};
App.Current.CheckAndDownloadUpdateAsync();
}

[ posted by ken ]


2010年07月28日

ショートカットメニューの表示

Silverlight4でショートカットメニュー(いわゆる右クリックで表示されるメニュー、コントロール名としては ContextMenu)を表示する方法です。(要 Silverlight Toolkit)

XAML上に定義しても結局メニューコマンド実行用のコードは裏に書かざるをえないので、Another TLではコードビハインドで動的にメニューを生成しています。
まぁ、本当はもっとスマートなやり方があるんでしょうけど、変にXAMLとコードが分散するよりは見通し良いかな、と今のところは考えています。見栄えをいじりたければスタイルだけXAML定義しておく、とかすれぱ対応できるかと。

ちなみに、MenuItemのHeaderプロパティに別のコントロールを突っ込めば好き放題にメニューをカスタマイズできますが、ヘンテコUIになる可能性があるのでご注意をw

というわけで、テキストボックスコントロールにコピペ用のメニューを付けるサンプル。
XAMLは端折っちゃいましたが、対象となるTextBoxコントロールにMouseRightButtonDownイベントだけ用意してあげてください。Toolkitの参照設定もお忘れなく。
// コピペ用ショートカットメニューの表示
private void openContextMenu(TextBox txt, double posX, double posY)
{
ContextMenu cm = new ContextMenu();

var miCut = new MenuItem()
{
Header = "切り取り"
};
miCut.Click += (s, e) =>
{
txt.Focus();
Clipboard.SetText(txt.SelectedText);
txt.SelectedText = "";
};
cm.Items.Add(miCut);

var miCopy = new MenuItem()
{
Header = "コピー"
};
miCopy.Click += (s, e) =>
{
txt.Focus();
Clipboard.SetText(txt.SelectedText);
};
cm.Items.Add(miCopy);

var miPaste = new MenuItem()
{
Header = "貼り付け"
};
miPaste.Click += (s, e) =>
{
txt.Focus();
txt.SelectedText = Clipboard.GetText();
};
cm.Items.Add(miPaste);

if (txt.SelectedText == "")
{
miCut.IsEnabled = false;
miCopy.IsEnabled = false;
}

cm.HorizontalOffset = posX;
cm.VerticalOffset = posY;
cm.IsOpen = true;
}

// 利用例
private void TextBox1_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
openContextMenu((TextBox)sender, e.GetPosition(owner).X, e.GetPosition(owner).Y);
}

[ posted by ken ]


2010年06月28日

カーソルループの高速化(2)

前回、current of を使用して高速化を図る方法について書いたのですが、
ここにヒントがあります。
前回のサンプルを見ていただくとわかるのですが、
update文が非常にシンプルな形になっていると思います。

カーソルループ中で実行されるSQL文をいかに簡単な形にするか、
という観点が比較的有効です。
つまり、join を最小限にし、キー項目をwhere節で指定して
やることなんですね。つまり、

insert文は、select句を使わずvalue句を使う。
update、delete文は、current ofを使う。

ということです。
具体的には、

カーソル宣言で、ループ内実行SQL文で使用する項目を定義し、フェッチする。
フェッチした変数を使用してSQL文を実行する。

ということです。

declare cursor curTest
select
a,b,c
from
tableB
join
tableC
on
   ・・・・・・      

open curTest

fetch next from curTest into @a,@b,@c

while (@@fetch_status = 0)
begin

update
tableA
set
c = c * 1
where
a = @a

if @@ROWCOUNT = 0 -- 更新対象が無ければInsert
begin
insert
tableA
values
(@a,@b,@c)
end

fetch next from curTest into @a,@b,@c

end

close curTest
deallocate curTest

これにより、カーソルオープン時のコストは多少かかりますが、
join時のコストが削減されます。
カーソルオープンは初回1回のみですが、joinは処理件数分行われますので、
高速化が可能になるというわけです。
しかし、これでカーソルループを使用しない処理よりも高速化が可能か、というと、
残念ながらそうではありません。

例えば、1万件を処理する場合、処理の内容にもよりますので、一概には言えないのですが、

カーソルループ使用:約160秒
カーソルループ未使用:約10秒

くらいの差がでてしまいます。
結局、繰り返しの処理は単純に1回の処理時間×処理件数になってしまいますので、
1回の処理時間をいかにはやくするか?という視点で高速化を行うわけですが、
やはり限界があるわけで・・・。

できるだけ繰り返しを使用せずに処理を記述することが、性能問題には有効です。

[ posted by H.K ]


2010年06月14日

カーソルループの高速化(1)

前々回、「ループ処理内で連番を振る」テーマについて書いたのですが、
「カーソルループ自体を高速化したい」という要望があったりします。
処理ロジックは変えたくない、でも速くしたい。
問題の本質は、多くの場合、性能試験がシステムテストの一部になっていることでしょう。
システムテストに入る時点で機能テストは終わっているわけですから、
処理ロジックは変えたくない、という意識が働くのはあたりまえのことです。
しかし、性能問題が発現した場合、その多くは「処理ロジックの大幅変更」を
伴うことが多いものです。

今回はカーソルループを残したままでの高速化を考えた場合、
どういう方法があるのか?ということで、「ループ処理を可能な限り排除する」という
本来の高速化の指針から言えば、少々例外的なトピックになってしまいますが・・・。
もちろん、大幅な変更ではないにしろ、ある程度の変更は必要になります。

カーソルループの処理を高速化するのに一番最初に浮かぶのは
カーソル自体を速くできないか?ということです。
T-SQLのカーソルにはいろいろなオプションがありますが、
あまり効果は期待薄。・・・。強いて言うなら、カーソル属性をreadonly にせず、
current of を使って更新するようなロジックを組むこと。
ま、これはカーソル対象テーブルを更新する場合とかにしか使えませんが。

declare cursor curTest
select
a,b,c
from
tableA

open curTest

fetch next from curTest into @a,@b,@c

while (@@fetch_status = 0)
begin

・・・・

update
tableA
set
c = c * 1
where current of cueTest

fetch next from curTest into @a,@b,@c

end

close curTest
deallocate curTest

あとはループのなかで行われているSQL文の最適化。
インデックスを試したり、実行プランを解析してjoin の条件順序の変更や
ヒントを付与したりして調整していくのですが、
多くの場合、なかなか性能は上がらないものです。

では、これ以上、何ができるのか?次回。

[ posted by H.K ]


2010年05月17日

INSERT文に "ORDER BY"

「レガシーシステムに渡すデータに連番を振る」という処理はSQLにとって
苦手な処理のひとつかもしれません。

そもそも、COBOLなどの一般的な事務処理言語はREAD~WRITEの繰り返しによっ
て処理が構成され、READとWRITEの処理の間(項目転送とか)で採番された
連番をアウトプットとして書き出す、というのはさほど難しい処理ではあり
ません。

しかし、同様の処理をSQLを使用して連番を振る、ということは繰り返し
(ループ)が前提となり、本来、SQLの文法を拡張したカーソル処理を行う
必要がある場合が多いものです。

以前からやってみたかったテーマだったのですが、SQLServerでT-SQLを使用
してカーソルを使用することなく、連番が振れないか?

以下のような作業用テーブルを用意します。
create table [dbo].[sWorkTable_SL] (
[TENPO] decimal(3, 0)
, [BUMON] decimal(2, 0)
, [JANCD] decimal(13, 0)
, [YMD] varchar(6)
, [SURYO] decimal(5,1)
, primary key clustered (
[TENPO]
, [BUMON]
)

create table [dbo].[sWorkTable_SL_SEQ] (
[TENPO] decimal(3, 0)
, [BUMON] decimal(2, 0)
, [JANCD] decimal(13, 0)
, [YMD] varchar(6)
, [SURYO] decimal(5,1)
, [SEQNO] int IDENTITY(1,1)
, primary key clustered (
[SEQNO]
)

[dbo].[WorkTable_BASE]から[dbo].[WorkTable_SEQ]にデータを写すとき、
[SEQNO]をIDENTITYとして定義しておき、任意の順番でINSERTすることができ
れば、連番を振れるわけですが・・・。

用意したSQLは

insert into
[dbo].[WorkTable_SL_SEQ]
( [TENPO]
, [BUMON]
, [JANCD]
, [YMD]
, [SURYO]
) select
[TENPO]
, [BUMON]
, [JANCD]
, [YMD]
, [SURYO]
from
[dbo].[WorkTable_SL]
order by
[TENPO]
, [BUMON]

というもの。INSERT-SELECT文にORDER BY句が付くと、ちょっと奇異な感じが
しますが・・・。

実行プランを見てみます。(クリックで拡大)

インデックスが合致している場合

上記のINSERT文では、ORDER BY句が[dbo].[WorkTable_SL]のインデックスと同じ
なので、少々わかりにくいですが、インデックスに掛からない(使用されない)
ような項目(例えば [SURYO]など)に変えてみると、(クリックで拡大)

インデックスが合致しない場合

おわかりですか?sortが現われます。つまり、order by句が効いているということ
になります。
今回の例は単純なので、すべての連番に応用できるものではないことも事実なの
ですが。
もうひとつ気になるのは性能です。ためしに18万件ほどでやってみました。

cursor-loop: 120秒
INSERT-Order by: 20秒

差は歴然です。
[ posted by H.K ]