定番イベントですね。所感はさておきスライドメインでぺたぺたしておきます。
https://channel9.msdn.com/
.NET
App Service の CI/CDでビルドエラー
ちょっとはまったのでメモ。(そのうち改善されると思われる)
はまった環境: Visual Studio 2017 で .NET Framework 4.6.2 なクラスライブラリでNuGetなパッケージを使用しているC#プロジェクト(NuGetはPackageReferenceを参照する)と、それを参照するASP.NETなアプリ(1つのソリューションにASP.NETとクラスライブラリなプロジェクトがある状態)
はまった理由: App Service上(Kudu上)で msbuild 15.x がないから(たぶん)
普通にApp Service上でCI/CDを使って(今回はLocal Git)上記なプロジェクトをデプロイ(git push)すると以下のようにNuGetで取得するパッケージに含まれるアセンブリが見つからない旨のエラーがでてビルドに失敗します。(nuget restoreやdonet restoreしてるにも関わらず)
省略 : remote: D:\Program Files (x86)\dotnet\sdk\2.0.0-preview1-005977\Microsoft.Common.CurrentVersion.targets(1964,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly "Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. [D:\home\site\repository\web1\Shared\Shared.csproj] : 以下略
カスタムデプロイスクリプトを使用するようにして、dotnet build や dotnet msbuild を使ってもダメです。
※Kudu上に現状Previewなmsbuildがありますがそちらを使ってもダメ
※ちなみにプロジェクトがPackage.configを参照するようになってたら問題なくビルドできます。PackageReferenceの場合のみおかしい。
回避策: クラスライブラリなプロジェクトファイル(.csproj)を新しい形式に変換する
変換は以下のURLを参考に。
最低限Project要素の属性を新しくして TargetFrameworkVersion を TargetFrameworkにしてv.4.6.2 とかを net462 とかにします。あとは不要な要素はバスバスけして、AssemblyInfo.csも消したりしました。
修正後、Visual Studio 2017上でちゃんとプロジェクトがロードできてビルドできればひとまず大丈夫かと思います。(Minなcsprojにしてから後でNuGetパッケージ追加したりもろもろいじってもよいかと思います。
変換後は dotnet build や dotnet publish で問題なくビルドできます。(つら)
過渡期な問題だと思いますが、もし嵌った人がいれば参考にしてもらえると。
dotnetConf 2016 Japan
7月9日(土)に dotnetConf の日本版である dotnetConf 2016 Japan が開催されました。(C#ユーザー会が主催です)
バナー画像はおいらが適当にコラりました。タイムテーブル的にはこんな感じですね。結構もりだくさんです。
※ 自分は裏方すらする気がなかったんですが岩永さんの無情な一言により(?)司会他やりました。
13:10 – 13:50 | チャックさん | dotnetConf 2016 Japan 開催にあたって ~ .NET の今と未来 |
14:00 – 14:45 | 岩永信之 | .NET Standard |
14:55 – 15:40 | 榎本さん+ちょまどさん | Xamarinの新しい話とMonoの深い話 |
15:50 – 16:35 | Brian Lagunas | MVVM Done Right with Xamarin.Forms and Prism |
16:45 – 17:30 | @tanaka_733 | .NET Core on RHEL |
17:40 – 18:25 | しばやん | ASP.NET Core |
18:35 – 19:20 | ぼんぷろ | .NET Core/VS/VSCode他ツール類 |
1発目のチャックさんがさらっと後続つぶししてたのが面白かったです(?)
岩永さんは安定、Xamarin話はEテレ、Braianはお茶目+貴重なPrism話、RHELは日本での貴重な内容、ASP.NET Coreは愛のある良いとこダメなとこ、ぼんぷろ先生はC#たん、みたいな感じでした(ひどいまとめ)
各セッション資料などは岩永さんのBlogなどにまとまってるのでそちらを参照ください。
- dotnetConf 2016 Japan / ++C++;
- dotnetConf Japan 2016 で登壇してきました! / ちょ窓帳
- dotnetConf 2016 Japan で ASP.NET Core 1.0 について話してきました
- Xamarinの新しい話とMonoの深い話 @ dotnetConfJapan2016 / ものがたり
Brianさんはほぼライブコーディングだった気がする。
当日の様子はTwitterのハッシュタグやYoutubeを追うといいかもです。
※Youtubeは最初のセッション、音声トラブルとかあったりして音が聞こえづらいかも。そのうちCh.9にあがるのを期待。
なお、Microsoftさんのご厚意により懇親会とサプライズケーキが用意されました。
https://twitter.com/aetos382/status/751961913924333568
間違えた、こっちです。
記念ケーキ。 #dotnetconf https://t.co/z2TBIKjxFx
—
++C++; // 管理人: 岩永 (@ufcpp) July 09, 2016
サプライズなのにケーキあることを若干漏らしてるちょまど氏
#dotnetconf
そろそろ終わりだ!
隣の部屋に大量のサンドイッチとピザとケーキがあるらしい! https://t.co/HCYxkyc1Qm
—
ちょまど@MS入社して3ヶ月 (@chomado) July 09, 2016
あと大量のピザ+サブウェイ!
大量。 #dotnetconfjp #dotnetconf https://t.co/Ffk4myj8tT
—
こくぶん (@masak) July 09, 2016
最後に集合写真をとりましたので置いておきますね。
#dotnetConfjp 2016 おつかれさまでした! theta360.com/s/40s6Po0htdKP…
—
こすもす.えび (@kosmosebi) July 09, 2016
なおこのイベント、本家dotnetConfのローカル版ということでちゃんと本家の公式にも載ってます。
そんな感じで面白かったです。(ちょっと長丁場すぎましたがw)
みなさまお疲れ様でした&ありがとうございました。
.NET Core 1.0 RTM / Visual Studio 2015 Update 3
予定されてた通り .NET Core 1.0 がRTMになりました。おめでとうございます。また関連するツール類なども更新されています。
Previewの文字が取れました。
- Visual Studio 2015 Update 3 and .NET Core 1.0 Available Now
- 全体的な概要はこちら
- Visual Studio ダウンロード
- Visual Studio Update 3 Release notes
- Known Issues
- Visual Studio 2015 Update 3はこちら。MSDNサブスクライバーダウンロードにはWith Update 3なISOイメージも増えてました。
- ※日本語ページはまだRCだった
- dot.net みればわかりますが、”dotnet”なDockerコンテナーのベースイメージも使えます。てことでDocker Tools for VS 2015もUpdateされました。
- Team Foundation Server 2015 Update 3
- こちらもUpdate 3がでています。
- .NET Core 1.0
- Announcing .NET Core 1.0
- .NET Core 1.0がRTMですね。
- WindowsでVisual Studio 2015使ってる場合はUpdate 3(現状)+.NET Core 1.0 VS 2015 Tooling (Preview 2)を入れるといいよとのこと
- コマンドラインなユーザーはこちら: the .NET Core SDK for Windows
- Visual Studio CodeでもOKです。C#拡張入れるといいでしょう。
- 詳細は @ufcpp さんことにぃにやdotnetConf 2016 Japanのセッション見ればいいんじゃないでしょうか。
- またドキュメントはdocs.microsoft.com上で公開・メンテナンスされるぽいです。
- ASP.NET Core 1.0
- Announcing ASP.NET Core 1.0
- ASP.NET Coreも1.0です。
- ASP.NET Core Documentation
- Entity Framework Core 1.0
- Announcing Entity Framework Core 1.0
- EF Core 1.0もRTMですね。
- そのほか
ちなみに Azure App Service Web Apps ではもうASP.NET Core 1.0が利用できますよ。
ASP.NET Core 1.0 is now Available on Azure Web Apps. #azure #dotnetcore
—
David Ebbo (@davidebbo) June 27, 2016
dotnetConf 2016 Day 1 Keynote
dotnetConfの時期ですね。.NETがメインのバーチャルイベントです。
昨年は.NET Coreになったりなんかいろいろあった気がします。日本でも本家イベントの後でイベントありましたね。チャックさんのサプライズ誕生日とかやってた気がします。
というわけで1日目キーノートの雰囲気でも。
It’s openness! [Connect(); とか Azure Updateとかいろいろ]
Connect(); というイベントがありました(まだ明日もあります)。とりあえず簡単に纏め。
※ニコ生(日本語であれこれ解説してくれたり)もあります → 池澤あやかと語る MS開発者イベント Connect(); 解説・生中継 by 週アス
動的にモジュールを読み込む
Windows Azure Blog Storage上にアセンブリを置いて動的に読み込んでみましょう。
何の変哲もないインターフェース用意して
適当に2つほど実装して個別のアセンブリを作っておきます。
MarshalByRefObjectがポイントですね。
ビルドしたアセンブリはBlobにUpしておきましょう。
一応Privateコンテナです。
さて今回はサンプルなのでWorkerRole内でアセンブリをロードして定期的に実行するようにします。
で、実際にアセンブリをロードするわけです。
private void LoadAssembly(string AssemblyUrl,string AssemblyTypeName) { if (SampleAppDomain != null) { AppDomain.Unload(SampleAppDomain); } PermissionSet trustedLoadFromRemoteSourceGrantSet = new PermissionSet(PermissionState.Unrestricted); AppDomainSetup trustedLoadFromRemoteSourcesSetup = new AppDomainSetup(); trustedLoadFromRemoteSourcesSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; SampleAppDomain = AppDomain.CreateDomain("SampleDomain", null, trustedLoadFromRemoteSourcesSetup, trustedLoadFromRemoteSourceGrantSet); var webpermission = new WebPermission( NetworkAccess.Connect, AssemblyUrl ); SampleAppDomain.PermissionSet.SetPermission(webpermission); SampleAppDomain.PermissionSet.Demand(); SampleAssembly = (ISample.ISample)SampleAppDomain.CreateInstanceFromAndUnwrap(AssemblyUrl, AssemblyTypeName); }
こんな感じですね。リモートホスト上にアセンブリがあるのでWebPermissionで権限を付けてます。それ以外はいたって普通。
あとBlobをプライベートコンテナに置いているので、共有アクセス署名(SAS: Shared Access Signature)を付けないとアクセスできません。
private string GetAssemblyUrl() { var assemblyName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobName"); var assemblyContainerName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobContainerName"); ; var connectionString = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobStorage"); ; var account = CloudStorageAccount.Parse(connectionString); var client = account.CreateCloudBlobClient(); var assemblyBlobContainer = client.GetContainerReference(assemblyContainerName); var policy = new SharedAccessPolicy { Permissions = SharedAccessPermissions.Read, SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5), SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromMinutes(10) }; CloudBlob blob = assemblyBlobContainer.GetBlobReference(assemblyName); var accessurl = blob.Uri.ToString() + blob.GetSharedAccessSignature(policy); Trace.WriteLine(accessurl); return accessurl; }
SAS付けた状態のURLを返してもらって、そいつをCreateInstanceFromAndUnwrapに渡します。
ポイントはアクセス期限の開始時刻を現在時刻からマイナス5分してるところぐらいですかね。。。Blob Storage上の時刻よりローカルPCのほうが進んでたために、アクセスした時刻が署名で許可してる期間外になってFileNotFoundExceptionになるというヒドイ目を見ました。ふぅ。
最後にロードしたいアセンブリの情報をサービス設定ファイルに書いておきましょう。
で、サービス設定ファイルが更新されたらアセンブリをロードしなおすようにします。
public override bool OnStart() { LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName")); RoleEnvironment.Changed += (s, e) => { if (e.Changes.Any(chg => chg is RoleEnvironmentConfigurationSettingChange)) { LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName")); } }; return base.OnStart(); }
とりあえずこんな感じ。
じゃ実行してみましょう。
起動した時はSample1.dllがロードされて、サービス設定ファイルをSample2.dllに変更してUpdateしたらちゃんとロードしなおされてますね!
あとは気が向いたらBlob上のDLLを入れ替えるなりしてロードしなおせばいいわけです。
もちろんロードした後はローカルのキャッシュで動作してるので、SASの期限切れても問題ないですしBlob上からファイルがなくなっても問題ないです。
拡張モジュールの実装とかいろいろ出来そうですね。AppDomainを工夫したらマルチテナントで遊べると思います。
おまけ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Net; | |
using System.Security; | |
using System.Security.Permissions; | |
using System.Threading; | |
using Microsoft.WindowsAzure; | |
using Microsoft.WindowsAzure.ServiceRuntime; | |
using Microsoft.WindowsAzure.StorageClient; | |
namespace WorkerRole1 | |
{ | |
public class WorkerRole : RoleEntryPoint | |
{ | |
private ISample.ISample SampleAssembly; | |
private AppDomain SampleAppDomain; | |
public override void Run() | |
{ | |
while (true) | |
{ | |
Trace.WriteLine(SampleAssembly.Execute()); | |
Thread.Sleep(5000); | |
} | |
} | |
public override void OnStop() | |
{ | |
if (SampleAppDomain != null) | |
{ | |
AppDomain.Unload(SampleAppDomain); | |
} | |
base.OnStop(); | |
} | |
public override bool OnStart() | |
{ | |
LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName")); | |
RoleEnvironment.Changed += (s, e) => | |
{ | |
if (e.Changes.Any(chg => chg is RoleEnvironmentConfigurationSettingChange)) | |
{ | |
LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName")); | |
} | |
}; | |
return base.OnStart(); | |
} | |
private void LoadAssembly(string AssemblyUrl,string AssemblyTypeName) | |
{ | |
if (SampleAppDomain != null) | |
{ | |
AppDomain.Unload(SampleAppDomain); | |
} | |
PermissionSet trustedLoadFromRemoteSourceGrantSet | |
= new PermissionSet(PermissionState.Unrestricted); | |
AppDomainSetup trustedLoadFromRemoteSourcesSetup = new AppDomainSetup(); | |
trustedLoadFromRemoteSourcesSetup.ApplicationBase = | |
AppDomain.CurrentDomain.SetupInformation.ApplicationBase; | |
SampleAppDomain = AppDomain.CreateDomain("SampleDomain", null, trustedLoadFromRemoteSourcesSetup, trustedLoadFromRemoteSourceGrantSet); | |
var webpermission = new WebPermission( | |
NetworkAccess.Connect, | |
AssemblyUrl | |
); | |
SampleAppDomain.PermissionSet.SetPermission(webpermission); | |
SampleAppDomain.PermissionSet.Demand(); | |
SampleAssembly = (ISample.ISample)SampleAppDomain.CreateInstanceFromAndUnwrap(AssemblyUrl, AssemblyTypeName); | |
} | |
private string GetAssemblyUrl() | |
{ | |
var assemblyName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobName"); | |
var assemblyContainerName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobContainerName"); ; | |
var connectionString = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobStorage"); ; | |
var account = CloudStorageAccount.Parse(connectionString); | |
var client = account.CreateCloudBlobClient(); | |
var assemblyBlobContainer = client.GetContainerReference(assemblyContainerName); | |
var policy = new SharedAccessPolicy | |
{ | |
Permissions = SharedAccessPermissions.Read, | |
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(–5), | |
SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromMinutes(10) | |
}; | |
CloudBlob blob = assemblyBlobContainer.GetBlobReference(assemblyName); | |
var accessurl = blob.Uri.ToString() + blob.GetSharedAccessSignature(policy); | |
Trace.WriteLine(accessurl); | |
return accessurl; | |
} | |
} | |
} |
ADSIで利用方法によってはリークするケースがある
ADSIで利用方法によってはリークするケースがあるとのことです。
[ADSI] 障害情報 : WinNT プロバイダを用いて ADsOpenObject() をコールするとメモリリーク発生
http://blogs.technet.com/jpilmblg/archive/2010/05/14/adsi-winnt-adsopenobject.aspx
この問題ですがADSIだけに限らず.NET Framework の System.DirectoryServicesを用いるアプリケーションも内部的にはADSIを利用しているので同様の影響を受けます。
参考までにSystem.DirectoryServicesがどんな構造になっているかの情報を@uikouさんからもらいましたので併せて記載しておきます。
System.DirectoryServices 名前空間
http://msdn.microsoft.com/ja-jp/library/system.directoryservices(VS.80).aspx
.NET Framework のディレクトリ サービス
http://msdn.microsoft.com/ja-jp/library/ms180826.aspx
S.DS名前空間はActive Directoryに対する操作を行うのに良く使いますので、よく利用されていると思います。
通常のアプリケーションであればそこまでシビアではないのかも知れませんが、24×365なWeb/サービスアプリや一度に扱うオブジェクト数が多いユーザーメンテ系のアプリは要注意ですね。
情報を提供して頂いた@uikouさんに感謝感謝~