Task で非同期処理をつくる(.NET Framework 4.0用) (゜A゜)
第3回 TaskクラスとPLINQ(Parallel LINQ) − @IT を参考に非同期処理を実装してみる。
なお、.NET Framework 4.5 、C#5.0から提供される async, await での実装はしていません。
.NET Framework 4.0 での実装時の参考にしてください。
環境
- Visual Studio 2010
- .NET Framework 4
- ASP.NET MVC 4
- NLog 2
目的
Webアプリで時間のかかる処理を実行しつつ、Responseは返して画面描画は完了させる。
並行処理
参考にした記事では、task.Wait() を処理待ちをしていたけどWebアプリ想定として処理待ちはしません。また、WebのResponse送信完了後も処理が継続していることを確認するためにnotify()を完了後に実行するよう、task.ContinueWith()で指定してます。
public class TaskService { private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); void notify(Task t) { logger.Info("ヽ(*゚д゚)ノカイバー {0} done", t.Id); } public void Execute() { var task = Task.Factory.StartNew(() => { // 長い処理 for (int i = 0; i < 10000; i++) logger.Info("ヽ(*゚д゚)ノカイバー"); }).ContinueWith(notify); for (int i = 0; i < 100; i++) logger.Info('A'); } }
クラスは以下の通り。ASP.NET MVC のコントローラです。
public class HomeController : Controller { public ActionResult Index() { ViewBag.Message = "ヽ(*゚д゚)ノカイバー"; var service = new TaskService(); service.Execute(); TaskService.Execute(); return View(); } }
出力したログの結果は以下の通り。
2012-09-21 01:05:06.4620 INFO A ~中略~ 2012-09-21 01:06:56.8451 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.8591 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.8722 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.8862 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.8862 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.9142 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.9292 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.9442 INFO ヽ(*゚д゚)ノカイバー 2012-09-21 01:06:56.9582 INFO ヽ(*゚д゚)ノカイバー 1 done
TFS2012自動ビルドでデプロイすると「'managedRuntimeVersion' プロパティが 'v4.0' に設定されています。このアプリケーションには 'v4.5' が必要です。」への対応方法 #tfsug
IIS8.0に.NET Framework 4.5 のアプリケーションをデプロイしようとしたらうまくいかなかったのでメモ。
環境
- Windows 8
- IIS8
- Visual Studio 2012
- Team Foundation Server 2012
- Visual Studio 2010 インストール済み
- TFSもVisualStudioも全部ローカルPC
現象
TFS2012からの自動ビルドでIIS8.0に ASP.NET MVC 4アプリケーションをデプロイしようとしたら以下のエラーが表示されます。
自動ビルドに設定した内容は過去エントリ(TFSの自動ビルドでWebアプリのデプロイ、配布パッケージの作成を同時に行う #tfsug - kaji_3's blog)参照。
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets (3847): Web 配置タスクに失敗しました。(使用しようとしているアプリケーション プールでは、'managedRuntimeVersion' プロパティが 'v4.0' に設定されています。このアプリケーションには 'v4.5' が必要です。)
解析
ただし、Visual Studio 2012 の発行ウィザードでデプロイすると成功し、TOP画面が表示されます。また、Web Deploy用発行パッケージを作成してバッチでデプロイした場合も成功します。
TFSからの自動ビルド時のデプロイにはMSBuildの引数オプションの「/p:DeployOnBuild=True」を利用しているため、MSBuild固有の問題ではないかと推測します。
対応
色々検索して、VisualStudio2010から2012にアップグレードしたらデプロイできなくなった方がいました。
MSBuild deploy failing after upgrade to .NET 4.5 - Stack Overflow
そこで紹介されているエントリを参考にします。
Sayed Ibrahim Hashimi - MSBuild, C#, Visual Studio, Training, and more - Visual Studio project compatability and VisualStudioVersion
どうやら、Visual Studio 2010 をインストールしている環境で Visual Studio 2012 をインストールすると、MSBuildの targetの定義ファイルがVS2010の方を参照してしまうようです。
なので、VS2012を参照するよう以下のオプションを
ビルド定義→プロセス→ビルドプロセスパラメータ→MSBuild引数に、
/p:VisualStudioVersion=11.0
を追加します。これでオッケー!!
TFSの自動ビルドでWebアプリのデプロイ、配布パッケージの作成を同時に行う #tfsug
TFSを利用していつも継続的インテグレーションをしてます。
ビルドが成功したら動作確認用サーバに上げたくなります。
そして検証が完了したらデプロイしたアプリケーションを受入テスト等の別環境にデプロイしたくなります。
そんな課題を MSBuild のオプションを指定するだけで楽に解決する事ができます。
環境
- Visual Studio 2012
- Team Foundation Server 2012
- IIS8
- Windows8(クライアントもサーバも兼ねる)
設定内容
対象とする自動ビルドのビルド定義を開き「プロセス」→「ビルドプロセスパラメータ」→「MSBuild引数」に以下を指定します。/p:MSDeployServiceUrl、/p:DeployIISAppPath の値は任意に変更してください。
/p:CreatePackageOnPublish=true /p:DeployOnBuild=True /p:DeployTarget=MsDeployPublish /p:MSDeployPublishMethod=InProc /p:MSDeployServiceUrl=localhost /p:DeployIISAppPath="Default Web Site/TargetApp"
(ここです)
配布パッケージは、ビルド出力の格納先フォルダにあります。
(格納先フォルダの場所は、「ビルドの規定値」→「ステージング場所」です。)
格納先フォルダの実行したビルド結果のフォルダ内、「_PublishedWebsites」フォルダの下にあります。
当然ですが、デプロイについてはIISにデプロイする権限があるユーザで実行する必要があります。ご注意ください。
Livet ViewModel のValidation で ValidationGroup を作成する
LivetというかWPFでの単項目のチェックの実装について悩んでいた時、Nine Works: WPFでのデータ検証 1:IDataErrorInfoを使ったデータ検証 というエントリを見つけたので参考にさせて頂きました。
しかし、実案件に導入した際、ASP.NET の ValidationGroup のようにValidation対象をグループ化したいという要望が出てきました。しかし、DataAnotation にはValidationGroup に該当するものを見つける事ができませんでした。そのため、DiplayAttribute に存在する GroupName を ValidationGroup として利用する機能を追加します。
環境
- Visual Studio 2010
- Livet 1.0.4
補足
- DisplayAttribute のメンバである、Groupnameを利用しますが、このプロパティの説明「UI でのフィールドのグループ化に使用される値を取得または設定します。」に則った利用法なのか調べたかわかりませんでした。利用時は、十分検証してくださいい。
追加した機能
- プロパティをグループ化し、検証時指定グループの検証結果だけ取得する。エラーメッセージも同様。
- 1つのプロパティに複数グループの指定もカンマ区切りで指定可能。
- isValidメソッドを利用した検証時、Errorプロパティに検証結果のメッセージが設定される
検証を行うViewModelクラス
// using 省略 namespace LivetWPFApplication.ViewModels { /// <summary> /// DataAnnotationによるプロパティの検証を可能とするViewModelです。 /// </summary> public class ValidationViewModel : ViewModel, IDataErrorInfo { protected Dictionary<string, string> _errors = new Dictionary<string, string>(); /// <summary> /// オブジェクトに関する間違いを示すエラー メッセージを取得します。 /// </summary> public string Error { get { return GetErrorMessage(String.Empty); } } /// <summary> /// DataAnnotationsを使ってデータ検証を行い /// 指定した名前のプロパティに関するエラー メッセージを取得します。 /// </summary> /// <param name="propName"></param> /// <returns></returns> public string this[string propName] { get { var result = new List<ValidationResult>(); if (Validator.TryValidateProperty( GetType().GetProperty(propName).GetValue(this, null), new ValidationContext(this, null, null) { MemberName = propName }, result)) { // エラー無し _errors[propName] = null; } else { _errors[propName] = result.First().ErrorMessage; } return _errors[propName]; } } /// <summary> /// 指定したグループに属するプロパティの値が妥当かを返します。 /// </summary> /// <param name="validationGroupName">検証を行うグループ</param> /// <returns></returns> public bool isValid(string validationGroupName) { var errorMessages = GetError(validationGroupName); // エラーメッセージの更新通知 RaiseErrorChanged(); return errorMessages.Count() == 0; } /// <summary> /// 指定したグループに属するプロパティのエラーメッセージのサマリを取得します。 /// </summary> /// <param name="validationGroupName">検証を行うグループ</param> /// <returns></returns> public string GetErrorMessage(string validationGroupName) { var errorMessages = GetError(validationGroupName); return String.Join(System.Environment.NewLine, errorMessages.Values); } /// <summary> /// オブジェクトのすべてのプロパティの値が妥当かを返します。 /// </summary> /// <returns></returns> public bool isValid() { return isValid(String.Empty); } // Errorの変更通知 protected virtual void RaiseErrorChanged() { RaisePropertyChanged("Error"); } /// <summary> /// 指定したグループのエラーを取得します。 /// </summary> /// <param name="validationGroupName"></param> /// <returns>エラーのプロパティ名、エラーメッセージ</returns> private Dictionary<string, string> GetError(string validationGroupName) { IEnumerable<KeyValuePair<string, string>> errorMessages; if (String.IsNullOrEmpty(validationGroupName)) { errorMessages = _errors.Where(x => !String.IsNullOrEmpty(x.Value)); } else { errorMessages = _errors.Where(x => IsContaindValidationGroup(x.Key, validationGroupName) && _errors[x.Key] != null); } return errorMessages.ToDictionary(x => x.Key, x => x.Value); } /// <summary> /// 対象プロパティが、指定したグループに属するかの結果を返します。 /// </summary> /// <param name="propertyName">対象プロパティ名</param> /// <param name="validationGroupName">指定を行うグループ</param> /// <returns></returns> private bool IsContaindValidationGroup(string propertyName, string validationGroupName) { var attributes = Attribute.GetCustomAttributes(GetType().GetProperty(propertyName), typeof(DisplayAttribute)); if (attributes.Count() == 0) { return false; } var displayAttr = attributes[0] as DisplayAttribute; return displayAttr.GetGroupName().Split(',').ToList().Exists(x => x.Trim() == validationGroupName); } } }
Validationを行うViewModelの実装例
public class TargetVM : ValidationViewModel { #region Value1変更通知プロパティ private string _Value1; [Display(Name = "値1", GroupName = "grp1")] [Required] public string Value1 { get { return _Value1; } set { if (_Value1 == value) return; _Value1 = value; RaisePropertyChanged("Value1"); } } #endregion #region Value2変更通知プロパティ private int? _Value2; [Required] [Display(Name = "値2", GroupName = "grp2")] public int? Value2 { get { return _Value2; } set { if (_Value2 == value) return; _Value2 = value; RaisePropertyChanged("Value2"); } } #endregion #region Value3変更通知プロパティ private string _Value3; [Required] [Display(GroupName="grp3,grp4")] public string Value3 { get { return _Value3; } set { if (_Value3 == value) return; _Value3 = value; RaisePropertyChanged("Value3"); } } #endregion public bool Click() { return isValid("grp1"); } public bool ClickGroup2() { return isValid("grp2"); } public bool ClickGroup3() { return isValid("grp3"); } public bool ClickGroup4() { return isValid("grp4"); } public bool ClickGroupAll() { return isValid(); } }
Oracle DataPump Export の expdp オプションでFLASH_BACK_TIMEを設定する
パラメータファイル経由ではなく、コマンドオプションで利用した場合、文字列のエスケープで苦労したのでメモ。
環境
Oracle 11g Database
expdp kaji/scott SCHEMAS=kaji DIRECTORY=kaji_DIR1 DUMPFILE=kaji_DATA.dmp LOGFILE=kaji_DATA.log FLASHBACK_TIME=\"TO_TIMESTAMP('2012/09/12 15:55:00', 'YYYY/MM/DD HH24:MI:SS')\"
Appcmd を利用してWebアプリケーションをIISに作成する
IISの設定は。。もう手でやりたくないです(しろめ
Microsoft Windows Server 2008リソースキット IIS 7.0編 (マイクロソフト公式解説書)
- 作者: Mike Volodarsky,Olga Londer、他,マイクロソフト(株),(株)トップスタジオ
- 出版社/メーカー: 日経BPソフトプレス
- 発売日: 2009/04/23
- メディア: 単行本
- 購入: 2人 クリック: 9回
- この商品を含むブログ (2件) を見る
IISのパフォーマンスが大幅劣化した際、参考にしたこの本ですがその中に Appcmd なるIISの設定をコマンドで出来る方法があるじゃないですか。
という事で Webアプリケーションを作成して、MIMEの種類を追加するバッチを作ってみました。
環境
- IIS 8
- Windows 8
バッチ
rem 管理者として実行すること cd %windir%\system32\inetsrv rem 存在していた場合、アプリケーションを削除して作成 appcmd delete app "Default Web Site/clickOnePub appcmd add app /site.name:"Default Web Site" /path:/clickOnePub rem 変更前dump appcmd list config "Default Web Site/clickOnePub" /section:staticContent > beforeConfig.txt rem 拡張子を登録 appcmd set config "Default Web Site/clickOnePub" /section:staticContent /+"[fileExtension='test',mimeType='application/test']"
【参考】
・Appcmd.exe (IIS 8)
・http://technet.microsoft.com/ja-jp/library/cc725608(v=WS.10).aspx
TFS 自動ビルドで Web 配置パッケージを作成するための設定
前提
- Team Foundation Server 2010
- Visual Studio 2010