'' is not a valid login or you do not have permission.対策

サイレントインストールでユーザのマシンにSQL Server 2008 R2 Express をインストールしているのですが、一部のPCでインストールに失敗するという事象が報告されてきていました。ログを見ると

'' is not a valid login or you do not have permission.

というエラーが出ているので、さっそくぐぐってみると、どうも「コンピュータ名とSQLサーバへのログイン権限を与えようとしているユーザ名が同名の場合に発生する」らしいです。


http://serverfault.com/questions/119875/is-not-a-valid-login-or-you-do-not-have-permission-on-sql-server-installatio


など、たくさんのところで同じことではまっている人が居ます。こんなのSQL Serverインストーラのバグじゃないのか、と思うのですが、とにかくそうなっている模様。PC名を変更すれば回避できることを確認しました。今回のケースではユーザにPC名を変えてくれ、というわけにもいかず、かといって別ユーザだと意味がないので、どうしたらいいのか……。


サイレントインストール時のオプションで、ADDCURRENTUSERASSQLADMIN="True"を指定しているからこうなっているのか? NETWORK SERVICEアカウントに権限が与えられれば、インストールユーザに与えなくても大丈夫なのかなあ。なんらかのSQLメンテナンスを伴う機能をリリースしたいときにログインユーザに権限がないことが後々問題にならないだろうなという心配もある……。困ったな。


これってConnectに登録されてないのかな。探してみる。

2011/4/7 追記

Connectにあった。これは「Windows issue.」なので、「修正しない」。えー。ちなみにWindowsの問題として、こんな理由が書かれています。うーん。ユーザ名をコンピュータ名にしようとしてエラーが出るなら、コンピュータ名の方を変更するときにもエラーを出すべきでは? 設定できるのに、特定のソフトウェアをインストールするときになって初めて不具合に気がつく、というのは問題な気がするが……

Administratorを設定することで回避できるというポストがあった。たしかにこれであればうまくいく。Administratorが無効だったりパスワードが設定されてなかったりすることに対応しないといけないけど……

そもそも、このエラーが起きたときに不完全な状態でSQLサーバがインストールされるのも問題だと思う。エラーなら、インストール全体をキャンセルして欲しい。

DebugとReleaseでApp.configを切り替える

ビルドイベントでconfigファイルをリネームするなどいろいろ方法があるようですが、そういったバッチファイルでやるよりも、こちらで書かれている方法がシンプルかなと思った。

http://www.timvw.be/easily-switching-between-appconfig-files-with-msbuild/

<propertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug Customer1|AnyCPU' ">
 <debugSymbols>true</debugSymbols>
 <debugType>full</debugType>
 <optimize>false</optimize>
 <outputPath>bin\Debug\</outputPath>
 <defineConstants>DEBUG;TRACE</defineConstants>
 <errorReport>prompt</errorReport>
 <warningLevel>4</warningLevel>
 <appConfig>App.Customer1.Config</appConfig>
</propertyGroup>

propertyGroupのなかにappConfigという要素を入れ、そこでその条件下におけるconfigファイルを指定するだけです。簡単(まあ、プロジェクトファイルを直接編集しないといけないですが)。

追記

ビルドイベントでapp.configを差し替える方法だと、ClickOnce発行時のビルドで走ってくれないようなので、やはり上記の方法のほうがよさげ。

Wix 3.6で巨大ファイルを含む多言語対応インストーラを作成中

いろんな課題をいっぺんに解決しようとしたから大変でした。

  • インストーラは多言語対応
  • インストール時にSQLサーバのデータファイルをアタッチしたい
    • ただし、データファイルはログファイル、セカンダリファイルを含む
  • Windowsサービスをインストールし、インストール後に起動したい
  • 2Gを超えるデータファイルをインストーラに含めたい

言語化

多言語はBootstrapを使って対応。実はここは後輩がやってくれたのでまだよくわかってないです。キャッチアップしておかなきゃ。

SQL Serverデータファイルの配置

SQL Serverのデータファイルの配置については、カスタムアクションを考えていたけど拡張ライブラリで対応できるようです。

.NET, Sitecore and setup development: Attach / Detach database during installationという記事が参考になりました。
配置はいいのですが、権限の問題がついて回ります。この記事だと

<util:PermissionEx GenericAll="yes" User="NetworkService" />

で解決していましたが、ぼくの環境では

<CreateFolder>
 <Permission Domain="NT AUTHORITY" User="NetworkService" GenericAll="yes" />
</CreateFolder>


でいけました。

Windowsサービスのインストール

つづいて、サービスのインストールですが、

                <File Id="HogeExe" KeyPath="yes" Source="Hoge.exe" />

                <ServiceInstall Id="HogeServiceInstall"
                                DisplayName="Hoger Service"
                                Name="HogeService"
                                ErrorControl="normal"
                                Start="auto"
                                Type="ownProcess"
                                Vital="yes"
                                Interactive="no"
                                />

                <ServiceControl Id="HogeServiceControl"
                                Name="HogeService"
                                Start="install" Remove="uninstall"
                                Stop="both"
                                />
              
              </Component>

ServiceInstall と ServiceControl のNameを同じにするのがポイントらしいです。

巨大ファイルのアーカイブ

さて、最後に、Cab形式だと2Gを超えるファイルをアーカイブできないことがわかりました。PackageタグのCompress属性をnoにするとインストーラ自体の圧縮がかからないですが、インストーラに含まれるファイルがmsiの外にたくさんできてしまいます。さすがにこれでは配布に困るので、これを自己解凍+自動実行形式の7zipをつかって、後からまとめることにしました。今回は商用利用なので、Explzhなどは使えず、コマンドラインで対応することにします。

ここはやねうらおさんの記事を参考にしました。2006年の記事ですが、いまもやり方は変わらないようです。

この圧縮作業はビルドイベントに入れて自動的に行うようにしました。

その他

ダイアログが、自己解凍+BootStrap+インストーラの三個も出るのが気持ち悪いですね。自己解凍のところは多言語化できないし。

WebMatrixでUmbracoを入れてみる

Umbracoが気になっていたものの、なかなか試す機会がなかった。そんなときに、WebMatrixが出てたので、ちょうど試すのに良い機会だと思って、まずはローカルに入れてみる。

インストール中に、SQL Serverにデータストアのテーブルを着るらしく、saパスワードを入れるところがあって、saなんて作ってなかったので、作ったり有効にしたりして少し手間取ったが無事入った。しかし、よくよく考えてみるとUmbraco の4.0系は仮想フォルダが使えないという話だったはず。最新の4.5じゃないと個人的なニーズが満たせないのだった。うーん、せっかく WebMatrix使ったけど、普通に入れ直した方がよさそう。というところでもうやる気ゲージはゼロになった(というか総量が少なすぎる)

それにしてもインストールは簡単だけど、Visual StudioとかでどうDevelpするのかよく分かっていない。そもそも、APS.NETも数年ぶりに触るので、そのへんから思い出し、慣れていかないとなあ。

DataGrid の WPF Toolkit と WPF 4 での、バインドされた変数の変更通知の挙動のデフォルト値が異なっている件

DataGridを閲覧専用の場合は特に問題はありませんでしたが、行の中がコントロールがあるものの挙動がどうもおかしい。調べてみたところ、Rowのプロパティをバインドした場合の挙動が異なるようです。

探してみると、掲示板にこんな投稿がありました。ここで返事として書かれているとおり、

The DataGrid control in WPF 4 is a little different with the DataGrid in WpfToolkit. By default, it updates the data after the selecting row changed, so we cannot get the new value in the RowEditEnding event .

We can solve this problem by setting the UpdateSourceTrigger of the binding, For example, we can set the UpdateSourceTrigger as PropertyChanged so that wen can get the new value in the RowEditEnding event.

挙動が変わっているようです。

Toolkitでは編集タイミングで変更通知がなされたのですが、WPF 4では、UpdateSourceTrigger=PropertyChangedを指定しない限り、プロパティの変更はバインド先に通知されない、ということのようですね。このせいで編集が全くViewModelに伝わらなかったわけですね。

うーん、細かいところで挙動の変更があるもんだなあ。

既存(v3.5 時代)のモデルにおけるアソシエーションを外部キーアソシエーションへ変更する方法

Entity Framework v1で外部キーによるリレーションは、アソシエーションとしてマッピングされていました。

こんなテーブルで、Products.CategoryIdがCategories.Idを参照しているとします。これを1.0で自動生成すると、アソシエーションが作られ、こんな感じになります(複数形を手で単数形に直した)

このやりかただと、Productには、CategoryIdというキーそのものはなくなり、代わりにCategoryのインスタンスそのものを参照できるようにアソシエーションが作られます。これはこれで思想としてはすっきりしています。ただ、実際にアプリケーションを作っていると、どうしてもCategoryId自体を参照したくなることがあります。これをv1でやろうとすると、EntityKeyから

(long)_product.CategoryReference.EntityKey.EntityKeyValues.First().Value;

みたいなことをやる必要がありました。めんどうです。

これに対して、Entity Framework v4では、外部キーであるCategoryIdを残したままアソシーエションを張れるようになるということでした。

さっそく4になって、既存のコードを移行させるべく試してみたのですが、既存のアソシエーションに自動的にスカラプロパティが定義されるのかと思いきや、どうやらそうではないようです。そもそも、外部キーを残したままのアソシエーションと、v1流の外部キーを残さないアソシエーションとでは、そもそも意味合いが異なるようなのです。前者(v4で採用された、外部キーを参照できるタイプのもの)を「外部キーアソシエーション」といい、後者(v1で使われていた旧来のもの)を「独立アソシエーション」と呼ぶようです(http://msdn.microsoft.com/ja-jp/library/ee373856.aspx を参照)。

というわけで、既存のEDMファイルがある場合は、単純に開き直せば外部キーが使えるということにはなりません。ひとつひとつのアソシエーションに対して次のような作業が必要になります。

  1. スカラプロパティCategoryIdを追加し、列マッピングを設定する

  1. ナビゲーションマッピングから「アソシエーションの選択」で該当のアソシエーションの線(Category)を選択する
  2. 「参照に対する制約」で「プリンシパル」はそのまま(Category)、「プリンシパルキー」を参照先のプライマリキー(例ではId)、「依存キー」を上で作成した外部キー(例ではCategoryId)として使うプロパティに設定

  1. アソシエーションの線を選択し、「マッピングの詳細」タブをクリック。「マッピングを削除する」を選択する


これを行うことで、無事既存のコードを移行させることが出来そうです。これを画面でやるの他ちょっと手間なので、XMLだけの問題だったら、スクリプトで一括置換すればいいかなと思ったんですが、コードビハインドにもなにやら新しいコードが生成されているようなので、必要なところだけ手でやろうかという気になっています。

「C#][WPF]ロードに時間がかかる際の非同期待ちあわせのやりかた


すぐに忘れてしまうのでメモ。

ViewModelのロジック側はさくっと非同期呼び出しをします。そのとき、ViewModelに待ち状態のフラグ(例だとIsWaiting)を持っておきます。

  this.IsWaiting = true;
  var action = new Action(Hoge);
  action.BeginInvoke(ar => { action.EndInvoke(ar); this.IsWaiting = false; }, null);

画面ロード時に表示するビジーアニメーションはDomain Parked With VentraIP Australiaからお借りしました。共通Resourceにしておくと便利ですね。

使うときはGridでレイアウトします。GridのRow、Columnを同じ番号にするとコントロールが重なるので、既存のコントロールの上にビジーアニメーションコントロールを重ねて使います。ActualWidth、ActualHeightでいっぱいに広げるといいのですが、重ねる元がAuto指定の場合、タイミングによっては値が確定する前にアクセスされてしまい、無限に広がってしまうので注意しないといけませんでした(うまくいくタイミングはよくわかってませんが……)。

<Grid>
  <StackPanel Name="target">ここにかさねる</StackPanel>
  <Control Visibility="{Binding IsWaiting, Converter={StaticResource BooleanToVisibilityConverter}}"
           Width="{Binding Path=ActualWidth, ElementName=target}" 
           Style="{StaticResource BusyAnimationStyle}"
           Background="Transparent"
   />
</Grid>

こんなかんじ。あ、BooleanToVisibilityConverterをStaticResourceに入れといてください。