ASP.NET Coreで作成したWebSocketサーバをAzure Event Hubsを使ってスケールアウトに対応してみたけど・・・
注意
2017年1月17日に大幅訂正を行いました。
今回実装してみた結果、WebSocketのスケールアウトにEvent Hubsを用いることは不適切です。
後半に何故EventHubsでスケールアウトを実装することが不適切であったかを書きました。
後日、より適切な実装を行ってみたいと思っています。
はじめに
前回の記事
で、ASP.NET Coreを使ってWebSocketチャットサーバを作成してみました。
ただ、前回の実装ではスケールアウトすることができませんでした。
1プロセスで1チャットサーバとなるためマルチプロセスなサーバで動かした時にはダメな感じになってしまいますしロードバランスで負荷分散した時などもダメダメです。
今回はAzure Event Hubsを使用してスケールアウトに対応したWebSocketサーバを作成してみました。
ソースコードはこちらになります。
解説
使用した主なライブラリ
NuGet Gallery | Microsoft.Azure.EventHubs 0.0.4-preview
.NET Standard対応しているEventHubs用のライブラリです。
現時点でpreview版です。
NuGet Gallery | Microsoft.Azure.EventHubs.Processor 0.0.4-preview
.NET Standard対応しているEventHubsからメッセージを受信する為のライブラリです。
現時点でpreview版です。
前回からの変更点
Comparing master...Backplane · yuka1984/aspnetcore_webspcket_sample · GitHub
前回サンプルからの差分になります。
Azure側での操作
Event Hubsの作成
まずはMarketPlaceからEvent Hubsを選択して作成を押下します。
Nameを入力した後にPrincing TierをBasicにします。
Basicのほうが安いです。
作成を押下します。
Event Hubsが作成できたらEventHubEntityの追加を行います。
EvnetHubの名前を入力して作成します。
Azure Blob Strageの作成
リソースグループにストレージアカウントを追加します。
作成したストレージアカウントでBlobコンテナーを追加します。
以上でAzure上での作成は終了です。
送信クラス
まずはAzure Event Hubsへ送信を行うクラスを作成します。
Azure Event Hubsライブラリを使用して送信します。
EHConnectionStringはEvent Hubs内のShared access policiesにて確認できるConnection string primary keyを設定します。
EHEntityPathには先ほど作成したEventHubEntityの名前を指定します。
前回、WebSocketの接続をObserver/Observableパターンで作成しましたので引き続き送信クラスもObsreverな形で実装しました。
受信クラス
Event Hubsへの接続は特殊な用途でない場合にはEventProcessorHostクラスを用いて受信します。
このクラスで受信を行うとパーティションへの接続の排他制御、オフセットの管理をAzure Blob Storageを用いて行ってくれます。
C# での Event Hubs の使用 | Microsoft Docs
こちらのページが参考になるでしょう。
しかし、スケールアウトでEvent hubsを用いるためには、EventProcessorHostを用いるとうまくいきません。
そこでDirectRecieveを使用します。
DirectReceiveはイベントの受信を低レベルで行うことが可能です。
今回はDirectEventReceiveManagerというクラスを作成して受信処理を行います。
Event Hubsに対して送信したメッセージはどのパーティションに送信されるかは基本的に不明です。
なんでスケールアウトを達成するためにすべてのパーティションを監視します。
ますはEventhubClient.GetRuntimeInformationAsyncにてEventHubsの情報を取得します。
PartitionIdsプロパティにてパーティションIdを取得することができます。
パーティション毎に監視を行う際にGetPartitionRuntimeInformationAsyncにてパーティションの情報を取得します。
LastEnqueuedOffsetプロパティにてこのパーティションにエンキューされた最後のオフセット情報を取得できます。
取得開始時にこのオフセットを用いることで起動以前のメッセージを受信しないようにします。
それ以降はCreateReceiverでレシーバーを作成し受信メソッド行い、受信が成功した場合にはオフセットを更新します。
DirectEventReceiveManagerはIObserver
チャットへの組み込み
ChatServerクラスを変更してEvent Hubsを用いてチャットメッセージのスケールアウトに対応します。
前回からの変更点として
SendChatMessageToEventHubsObserverクラス DirectEventReceiveManagerクラスのインスタンスをもって
CreateEventProcessorの戻り値にChatMessageProcessorのインスタンスを常に返すように実装。
また前回は 送信/受信をObserver/Obesrvableで実装したWebSocket管理クラスを相互にSubScribeし合うことでチャットを実現していましたが、管理クラスのSubscribeにEventHubsObserverを繋げることで、チャットメッセージを受信した場合にEvent Hubsに送信されるように変更し、DirectEventReceiveManagerのSubscribeに管理クラスを繋げることでEvent Hubsからメッセージを受信した場合にWebSocketで送信が行われるように変更しました。
Observer/Observableパターンで実装すると繋ぎ方の変更で処理を変更できたりします。
このチェインが実装していると楽しいです(^◇^)
最後にstartupクラスにてChatServer.EventRecieveEventAsyncを実行してDirectEventReceiveManagerクラスのRecieveAsyncを実行してすれば完了です。
この実装の問題点
今回の実装には大きな問題点があります。
実は一つのPartitionへの接続は最大5クライアントまでしか行うことができません。
要するに今回の実装ではWebSocketサーバ5プロセス分までしかスケールアウトすることができないのです。
標準的な受信を実現してるEventProcessorHostでは、Partitionへの接続の排他制御をAzure Blob StorageとEpoch受信という仕組みを用いて行っています。
Epoch受信はpartitionへの接続を行う際にlong値であるEpochを指定して接続を行います。
Epoch接続を行った場合、Epoch値が大きい接続が優先して接続されます。
例えばEpoch10のクライアントがパーティション1に接続していて他のクライアントがEpoch20で接続を行ってきた場合、Epoch10は切断されEpoch20が接続されます。
またEpoch20が接続されている状態でEpoch10のクライアントが接続を行った場合、Epoch10クライアントは接続を行うことができません。
DirectRecieveでもエポックに対応した接続を行うことが可能ですが、別途クライアント間でパーティション毎のエポック管理を行う必要があります。
このようなことから、Event Hubsはスケールアウトのバックグラウンドとしては適切でありませんでした。
最後に
次回は頑張ります(´・ω・`)
今回は以上になります。
ではでは
ASP.NET CoreでWebSocketを使用するサンプルを作ってみたよ( `ー´)ノ
ごあいさつ
新年あけましておめでとうございます。
2017年もよろしくお願いします。
正月太り(´ ・ ω ・ `)
本題
SignalRではなくASP.NET CoreでWebSocketを使ったサンプルアプリを書いてみました。
今回作ったコードはこちらにあります。
ASP.NET CoreでWebSocketサーバを作るには
を使用すれば簡単に作成することができます。
今回のサンプリアプリはクライアントサイドにはVue.jsを使用しています。
解説
完成品の動き
アクセスしたらユーザ名の入力を行って接続を行います。
接続後にチャットで発言を行うと他のクライアントにも表示されてチャットができます。
接続、切断したユーザがいる場合にはその都度メッセージが送信されます。
参考にしたサイト
基本的にここに書かれている通りに作っていけばよい感じです。
ASP.NET CoreでVue使う場合にどうすればよいか考えるのに参考にしました。
使用したnugetパッケージ
- Microsoft.AspNetCore.WebSockets.Server
- Microsoft.AspNetCore.StaticFiles
- System.Reactive
- Newtonsoft.Json
サーバサイド
基本的に前述の参考サイトの通りに作成します。
今回はChatClientクラス ChatServerクラスの2クラスで構成しました。
ChatClientクラス
aspnetcore_webspcket_sample/ChatClient.cs at master · yuka1984/aspnetcore_webspcket_sample · GitHub
System.Net.WebSockets.WebSocketクラスを内包してクライアントとの通信を行うクラスです。
RecieveJoinAsyncでチャット参加メッセージを受け取って、RecieveAsyncでチャットメッセージを継続して受け取ります。
WebSocketのRecieveAsyncメソッドの引数にArraySegmentが使用されています。
WebSocketってプロトコルの仕様上メッセージが分割されることがあるのですが、連続してデータを取得しやすくするためにArraySegmentを使用してバッファ内でセグメント毎に受信します。
単純なテキストメッセージで分割するようなパターンってないと思いますが。
今回はObserver/Observableパターンで送信と受信を実装を行ってみました。
RecieveAsync中に受信したメッセージはIObservable.Subscribeにて購読を行っている人に配信されて、WebSocketがクローズされた場合にはOnCompleteが流れるて購読が解除される形になります。
OnNextが呼ばれるとメッセージをクライアントに送信します。
次にIObserver/IObservableをどのように利用しているかを見てみます。
ChatServerクラス
aspnetcore_webspcket_sample/ChatServer.cs at master · yuka1984/aspnetcore_webspcket_sample · GitHub
public void Map(IApplicationBuilder app) { app.UseWebSockets(); app.Use(Acceptor); }
マップ関数にてIApplicationBuilderへの登録を行っていてリクエストが来た時にAcceptor関数が実行されます。
Accepter関数はTask型になっていますが、この関数が終了するとHttpリクエストが返答されて終了します。
WebSocket接続だった場合にはクローズされます。
ですのでWebSocketを繋ぎっぱなしにしておくにはawaitで留まっている必要があります。
Accepter関数はこのようになっているのですが
まず最初にWebSocketリクエストなのかを判定しています。
そうでなかった場合はMiddlewareチェインの次を実行して関数を終了しています。
これは別にチェインの次を実行する必要はなくてhttpcontextのResponseにステータスコード400を指定してBadReqestにして終了してしまったりしてもOKです。
WebSocketリクエストであった場合には AcceptWebSocketAsyncを実行してWebSocketクラスを取得します。
このWebSocketクラスを使用して、先ほど説明したChatClientクラスを作成します。
ChatClient.RecieveJoinAsyncを実行してチャット参加メッセージを受信します。
参加がOKだった場合には接続中のクライアントに新規参加メッセージを送信し、クライアント間での接続を行っています。
// ほかのクライアントと相互接続 x.Subscribe(client); client.Subscribe(x);
この部分でIObserver/IObservableパターンが活きています。
クライアント間でお互いに購読登録を行うことで、メッセージストリームが流れた時にそのまま送信が行われます。
今回はそのままお互いにSubscribeしていますが、例えばWhereをいれて流れるメッセージを制御したりSelectでメッセージを調整したり色々な事が出来たりします。
Reactiveプログラミング面白い(^^)/
相互接続が完了したら受信待機します。
これだけでサーバ側でチャットが成り立ってしまいます(^◇^)
aspnetcore_webspcket_sample/Startup.cs at master · yuka1984/aspnetcore_webspcket_sample · GitHub
あとはStartupクラスでmiddlewareの登録を行ってあげればOKです。
クライアントサイド
aspnetcore_webspcket_sample/index.html at master · yuka1984/aspnetcore_webspcket_sample · GitHub
このファイル一つです。さぼってhtmlファイルの中にコードも書いちゃいました。
Vue.jsとかbootstrapとか取り込んでいる部分は前述の参考サイトを見てください。
実際のスクリプトはこんな感じです。
あんまり工夫もなくoldなJavaScriptな感じで大変申し訳ないです。
新しめのJavaScriptは今勉強中です。
Htmlはこんな感じ。
結構良い感じで書けます。
Vue.jsって凄くよくできてて感心してます。
今回はあまり使っていませんがC#のMVVMでReactivePropertyを使用したパターンをVue.jsだけで実現できる感じなんですよね。
今回は以上です。 ではでは(^^)/
ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ
ごあいさつ
年末ですね。皆さんはいかがお過ごしでしょうか。
今年もクリスマスとかいう私には縁の無いイベントを無事何事もなく家で寝て過ごし、来年に向けて断捨離を進めなければいけないのに全く進まず、色々とやってみたい事だけが溜まっていって一つも消化できていない、そんな年末を過ごしています\(^o^)/
はじめに
最近。WebフロントエンドとAzure Mobile AppとAzure Functionsの組み合わせでついぶくというサービスを作っていて今はWebフロントエンドの勉強などをしています。
4年くらい前で止まっていたWebフロントエンドの知識を、今時な?Webフロントエンドな知識に更新することが一つの目標です。
暫くはサービス開発で得た知識を記事にしていきたいと思います。
今回はVisualStudio2015でASP.NET WebAPIプロジェクトにてVue.js + Bootstrapな開発環境の構築を行ってみたいと思います。
インストール関連
まずはnode.jsをインストールします。VS2015にてnode.jsをインストールすることも可能なのですがバージョンが古くて(´・ω・`)なので最新版を入れてしまいます。
コマンドプロンプトにてnode -vを実行してインストールを行ったnodeバージョンが出ればOKです。
次にVisual Studioを開き[ツール]-[オプション]-[プロジェクトおよびソリューション]-[外部Webツール]にて
PATHの順位を一番に持っていきます。
さらに[ツール]-[拡張機能と更新プログラム]にて NPM Task Runnerをインストールします。
さらにWeb Compilerをインストールします。
プロジェクト作成
まずはWeb APIプロジェクトを作成します。
次のBundle処理の削除と余分なnugetパッケージの削除を行います。
BundleConfigクラスのRegusterBundle関数内をコメントアウトしてしまってください。
nugetパッケージマネージャにて以下のパッケージを削除します。
- bootstrap
- jquery
- Modernizr
- Respond
次にViews/Shared/_Layout.cshtmlにてBundleしたファイルの読み込みコードを削除してしまいます。
この状態にてデバッグを開始すると
スタイルがない状態になります。
Bootstrapの組み込み
今回はBootstrapをそのまま使うのではなく
を適用して、さらにscssにてスタイルの管理を行えるようにしてしまいます。
方法はこちらの記事を参考にしました。
まずはpackge.jsonをプロジェクトに追加します。
中身はこんな感じ
{ "version": "0.0.0", "name": "vuesample", "private": true, "devDependencies": { "grunt": "~0.4.5", "grunt-bower-task": "~0.4.0" } }
package.jsonはnpmというnode.js版nuget的なパッケージ管理ツールの設定を記述するためのファイルです。
作成しましたらタスクランナーエクスプローラよりInstallを実行します。
ここから表示できます。
次にbower.jsonファイルをプロジェクトに追加します。
bowerはWebフロントエンドに特化したパッケージマネージャーです。
bower.jsonはその設定ファイルになります。
中身はこんな感じ
{ "name": "VueSample", "private": true, "dependencies": { "jquery": "^3.1.1", "bootstrap-sass": "3.3.7", "Honoka": "^3.3.7" }, "exportsOverride": { "jquery": { "js": "dist/*.*" }, "bootstrap-sass": { "bootstrap.scss": "assets/stylesheets/bootstrap", "bootstrap.fonts": "assets/fonts/bootstrap/*.*", "js": "assets/javascripts/bootstrap.*" }, "Honoka": { "honoka.scss": "scss", "honoka.css": "dist/css", "honoka.font": "dist/fonts", "honoka.js": "dist/js" } } }
exportsOverrideという設定が重要です。 これにより各パッケージのファイルを、展開用にグループ分けしています。
次にgrundfile.jsというJavaScriptファイルをプロジェクトに追加します。
中身はこんな感じ
/// <binding /> module.exports = function (grunt) { grunt.initConfig({ bower: { install: { options: { targetDir: "", cleanTargetDir: false, layout: function (type, component, source) { if (type === "js") { return "Scripts"; } else if (type === "honoka.scss") { return "Content"; } else if (type === "bootstrap.scss") { return "Content/honoka/bootstrap"; } else if (type === "bootstrap.fonts") { return "fonts"; } else { return "__untyped__"; } } } } } }); grunt.registerTask("default", ["bower:install"]); grunt.loadNpmTasks("grunt-bower-task"); };
先ほどbower.jsonのexportsOverrideにて設定したグループごとにレイアウトを設定しています。
これによりreturnで返答しているフォルダにファイルが展開されます。
タスクランナーエクスプローラよりgruntfile.jsのbower:installを実行してください。
もしoperation not permitted, mkdir というエラーが出ていたら手作業でフォルダを作成して再度実行してください。
これらのファイルをプロジェクトに取り込みたいのでcsprojファイルを少し修正します。
csprojファイルをエディタで開いて以下を追加します。
<Content Include="Content\**\*.scss" /> <Content Include="Scripts\**\*.js" /> <Content Include="Scripts\**\*.map" /> <Content Include="fonts\**\*.*" />
これによりプロジェクトツリーのContentフォルダ下にbootstrap.scssファイルが追加されていますので右クリックしてコンパイルをしてみてください。
cssファイルが出来上がっていると思います。
ではViews/Shared/_Layout.cshtmlファイルに作成したcssファイルを指定してデバッグを実行します。
これでようやくスタイルが適用できた状態です。
ここからVue.jsの組み込みと簡単なコーディングを行っていきます。
Vue.jsの組み込み
まずは先ほど作成したpackage.jsonに追記を以下のように行ってください。
"version": "0.0.0", "name": "vuesample", "private": true, "dependencies": { "vue": "^2.1.7", "vue-route": "^1.5.1" }, "devDependencies": { "webpack": "^1.14.0", "webpack-dev-middleware": "^1.9.0", "webpack-hot-middleware": "^2.13.2", "webpack-merge": "^2.0.0", "babel-core": "6.21.0", "babel-loader": "6.2.10", "babel-preset-es2015": "^6.13.2", "grunt": "~0.4.5", "grunt-bower-task": "~0.4.0" }, "scripts": { "webpack": "webpack -d" } }
webpackというツールとbabelというツールのインストール、Vue.jsのインストールを行う設定を追記しました。
webpackは色々なライブラリを参照追加して作成したコードをコンパイルしてブラウザで実行できるスクリプトにしてくれるツールです。
それだけでなくスタイルとか画像とか色々と凄い事をしてくれるのですが詳しくは調べてみてください。
babelは新しい書き方をしているJavaScriptを古い形(ブラウザが理解できる形)にコンパイルしてくれるツールです。
追記後タスクランナーエクスプローラからpackageのインストールを行ってください。
次にプロジェクトにwebpack.config.jsというファイルを追加します。
中身は以下のようにしてください、
var path = require('path'); module.exports = { resolve: { root: path.resolve('./'), alias: { 'vue$': 'vue/dist/vue.common.js' } }, entry: { home: 'VueModels/home/Index.js' }, output: { path: "Scripts/app", filename: "[name].bundle.js" }, themeLoader: { themes: ['./node_modules/vuestrap/theme/bootstrap.scss'], // docs theme, default bootstrap }, module: { loaders: [{ test: /\.js$/, loader: 'babel' }, { test: /\.html$/, loader: 'html' }, { test: /\.json$/, loader: 'json' }] } };
次にプロジェクトに以下の画像のようにVueModelsフォルダ、その下にHomeフォルダを作成し、HomeフォルダにIndex.jsファイルを作成してください。
中身はこのような感じにしてください。
var Vue = require('vue'); var model = { message: "ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ" }; new Vue({ data: model, }).$mount('#app');
そうしたらタスクランナーエクスプローラからpackage.json/Custom/Webpackを実行してください。
もしWebPackというタスクがないのであればVisualStudioを再起動してみてください。
するとScriptsフォルダの下にapp/home.bundle.jsというファイルが作成されているかと思います。
これがIndex.jsをwebpackでコンパイルしてブラウザで使用できる形にしたファイルです。
Viewを修正して動かしてみましょう。
まずは_layout.cshtmlを修正します。
- classにContainerが設定されているdivタグにid=appを追加
- Script.Renderでコントローラ名.bundle.jsファイルを追加
次にHome/Index.cshtmlを修正します。
Jumbotronの中のPタグの中身を{{message}}と書き換えます。
これでデバッグ実行すると
とVueクラスとhtmlがバインドされるようになります。
ちょっとプログラムしてみよう
では、ちょっとそれっぽくプログラムしてみましょう。
ですがその前に、開発しやすくするためのタスクを追加します。
package.jsonを開きscriptsに"watch": "webpack -d -w"を追加しタスクランナーエクスプローラで実行します。
これを実行するとindex.jsファイルを監視し変更があった場合に自動的にビルドを行ってくれるようになります。
まずはIndex.jsをこのように修正してみましょう。
var Vue = require('vue'); var model = { message: "ASP.NET MVCでVue.jsを使用するWeb開発環境を作って動かしてみよう('ω')ノ", speakers:[] }; new Vue({ data: model, methods: { LoadSpeakers: function() { fetch("http://demo4404797.mockable.io/speakers") .then(function (response) { return response.json(); }) .then(function (json) { model.speakers = json; }); } } }).$mount('#app');
modelクラスにspeakersというプロパティを追加します。speakersは配列として宣言しておきます。
次にvueクラスにmethodsを追加しLoadSpeakersメソッドを追加します。
LoadSpeakersメソッドではfetchを使ってWebAPIにアクセスしてjsonを取得してmodelクラスのspeakersにセットします。
http://demo4404797.mockable.io/speakersにアクセスすればjsonを確認することができます。
次にIndex.cshtmlを修正します。
<div class="jumbotron"> <h1>ASP.NET</h1> <p class="lead">{{message}}</p> <p><button v-on:click="LoadSpeakers" class="btn btn-primary btn-lg">Get Speakers »</button></p> </div> <div class="row"> <div class="media" v-for="speaker in speakers"> <a class="media-left"> <img style="width: 128px" class="media-object" v-bind:src="speaker.Avatar"> </a> <div class="media-body"> <h4 class="media-heading">{{speaker.Name}} ({{speaker.Title}})</h4> {{speaker.Description}} </div> </div> </div>
jumbotron内にあったaタグをbuttonタグに書き換えてv-on:click="LoadSpeakers"を追加します。
これによりボタンがクリックされた場合には先ほどのLoadSpeakersメソッドが呼ばれます。
次にjumbotronに続くrowの中身をすべて消しmedia objectを追加します。
media objectはbootstrapのLayoutで画像付きの紹介文を書いたりするのに適しています。
mediaクラスを設定したdivタグにv-forアトリビュートを追加し speakersプロパティをループするように設定します。 これによりリストレンダリングを行うことができます。
子要素ではimgタグにv-bind:srcで画像アドレスをバインド、h4タグには名前や役割を表記、説明文にはDescriptionをバインドさせました。
では、デバッグ実行してみましょう。
ボタンを押してLoadSpeakersが実行されると
このような感じになると思います。
おしまいに
私自身始めたばっかりで全然知識が追い付いていない状態ですが、特に環境構築に関しては苦労しています。
この先VSでもWebフロントエンド開発が行いやすいように色々と進化していってくれると嬉しいなぁと思います。
ではよいお年を( `ー´)ノ
ついぶくテストにご協力いただいている方へ
ごあいさつ
クリスマスイブですね\(^o^)/
本題
仕様変更に関して
ついぶくテストにご協力いただいている皆様、本当にありがとうございます。
この度まとめツイートをツイートする方法を変更いたします。
こちらのアカウントよりユーザ様へメンションツイートにてまとめツイートを行います。
いったんユーザ様へメンションツイートを行い拡散を行うかはユーザ様に判断して頂きRTを行ってください。
この変更はついぶくアカウントをフォローした場合に適用されます。
フォローを行わなかった場合には、これまでと同様にユーザ様のツイートとしてまとめツイートを行います。
変更理由
7時に一斉にまとめツイートを行うという仕様はTLを荒らしがちになってしまうため。
以上、よろしくメリークリスマス('ω')ノ🎁🎄
ついぶくを公開しました。
ごあいさつ
断捨離が全然進みません(´・ω・`)
ついぶく
私事で大変恐縮なのですが
このたび「ついぶく」というサービスを試験リリースいたしました。
どういうサービスかと言いますと
1日のツイートの中からリンクを抽出してまとめページを作成してくれるサービスです。
サービスに参加すると朝7時頃にこのようなツイートが行われます。
12月20日のツイートをまとめたよ('◇')ゞhttps://t.co/TkWwWEtMso
— ゆうか🍣=3 🍣=3 🍣=3 (@yu_ka1984) 2016年12月20日
でリンクに飛んでみてもらえればわかるのですがツイート中からリンクを抽出し簡単にまとめてあります。
これだけのサービスです。
作成の経緯
理由1
私は結構ツイ廃なので1日に沢山ツイートします。
その中には技術的な調べ物をしていてそのリンクをツイートしたりしたものも含まれてます。
そんな中で1日単位くらいでまとめておけると楽だなぁというのが1つ目の理由です。
理由2
それだけだとはてブを使用すれば良いのですが
例えば
キュレーション活動としてフォローしている方に定期的に公開したいようなケースでははてブは少し使いづらいかな、という面があります。
ということでただツイートしていれば自動でまとめてくれるサービスがあったらなぁと思ったのが2つ目の理由です。
理由3
現在はWebフロントエンド技術の知識更新作業を行っているので勉強用にサービスを作りたかった。
Azure Functionsの勉強をするためにFunctionsを使用するサービスを作りたかった。
クラウド環境でのサービスデプロイに関して勉強するためにサービスを作りたかった。
以上の理由によりサービスの作成を行っています。
試験に協力して頂けるユーザさんを募集しています。
現時点で試験段階です。10人程度ユーザさんが使っていただけれるとうれしいかなぁとか思っています。
ただまだまだプログラム書いただけ~みたいな段階ですので色々と不具合も出てしまうかもしれません。
よく記事等のリンクをツイートされる方で協力して頂ける方がおりましたら、以下のサイトから登録してくれると嬉しいです。
ついぶく登録
https://yukasantestnet.azurewebsites.net/
こちらのサイトに移動するとツイッター認証画面が表示されます。
認証して頂いた後に設定画面に移動して「使います」ボタンを押していただければ登録が完了します。
以上、久々にプログラムがないブログ更新でした('ω')ノ
Azure Mobile Appsの.NETバックエンドでMVCを同居させてみよう🍣=3
ごあいさつ
衣類乾燥機が今すぐ欲しいのです(´・ω・`)
本題
はじめに
Azure Mobile Appsの.NET バックエンドはASP.NET Web APIがベースとなっていてMVCを追加できません。
でもMobileAppでもMVC使えるようにしたいですよねぇ。
ほらアプリの入口はやっぱりブラウザが多いと思いますし私がいま取り組んでいる課題で必要ですし。
ということで、Mobile AppでMVCを使えるようにしてみます。
前提
Mobile Appの.NET バックエンドプロジェクトをVSで作成してAzureにデプロイできている状態まで出来ている事を前提とします。
この辺を参考にすればすぐにできるかと思います。
コントローラーを追加する
Controllersフォルダに空のMVC5コントローラを追加します。
今回はPageControllerという名前にしてみました。
するとASP.NET MVCを構築するためのファイルが勝手に展開されます。
Viewを追加する
Viewを追加しちゃいます。
ルーティング設定を変更する
デフォルトコントローラ名を指定します。
これを行うとドメイン名のみのURL指定時のコントローラを指定できます。
MobileAppのスタートアップを変更する
今回の肝の部分です。
現在の状態でデバッグをスタートするとこのページが表示されると思います。
これはMobileAppのライブラリが表示しているページです。
このページを表示されないようにしMVC側のルーティング設定が適用されるようにします。
App_Start下にあるStartup.MobileApp.csを開きConfigureMobileApp関数をこのように変更します。
どういう意味かというと
UseDefaultConfigurationというMobileAppConfigurationに対する拡張メソッドを分解しAddMobileAppHomeControllerという拡張メソッドを呼ばないようにしています。
AddMobileAppHomeContollerによってMobileAppの機能に関係ないルーティングが設定されてしまうためです。
これにより
デバッグ実行すればPageコントローラのIndexがコールされIndexページが表示され、この状態でもMobileAppのAPIは正しく動作しているかと思います。
ではでは('ω')ノ
Prism.FormsでランダムにNavigationが変わるアプリを作ってみた(^^)/
こちらの記事は
8日目の記事になります。
初心者枠ということでお手柔らかにお願い致します。
Xamarin.Formsのナビゲーション
Xanarin,FormsのNavigationには大きく分けて4種類あります。
- NavigationPage
- MasterDetailPage
- TabbedPage
- CarouselPage
NavigationPageは非常にシンプルで1本道なナビゲーションです。
それに対してTabbedPage / CarouselPageはPageを横に並べます。
MasterDetailPageはMasterPageでページを選んでDetailにPageを表示するようなNavigationが可能です、
一般的に標準的なアプリにおいてiOSはTabナビゲーション、AndroidはMasterDetailが採用されていることが多いです。
これは各プラットフォームの推奨デザインによります。
同じアプリでもプラットフォーム毎にナビゲーションを最適化しておく事は大切です。
Xamarin.Formsはクロスプラットフォームで同じアプリを作る仕組み・・・・
ナビゲーションを簡単に変更するなんて出来ないんじゃないの・・・・?
そんなお考えのそこのあなた(; ・`д・´)m9
今回ご紹介する方法ならできるんです\(^o^)/
それでは方法をご紹介してみましょう。
Prism.Formsの利用
今回のサンプルではPrism.Formsを使用します。
Prism.FormsはMVVMライブラリでかなり巨大なライブラリになります。
Prismに関して、なにそれ美味しいの?という方は
こちらをご覧ください、
はじめに
考え方
TabbedPageを右に90度回転させるとMasterDetailっぽくありません?
そういう事です。
二つのアプローチ
TabbedPageを継承してCustomRendererでMasterDetailのような見た目のNavigationを作成する。
MasterDetailPageを継承してTabbedPageに近いインターフェースと仕組みを持ったクラスを作成する。
1番が理想形です。が・・・すいません。完成しませんでした・・・
今回は2番のアプローチで作成を行います。
プログラム
今回のサンプルプログラムはこちらになります。
MasterDetail拡張
MasterDetailクラスを継承し更に IViewContainer
この3つはabstractクラスであるMultiPage
実際には今回の範囲ではこのインターフェースを持つ必要はないのですが形だけでも似せるために実装しています。
MasterPageにはListViewをおいてChildrenと結び付けを行っています。
Childrenが選択された場合にはDetailに該当ページをNavigationPageに包んでセットします。
そのほかChangePageなどを実装しています。
PageNavigationServiceの拡張
本来であればPrism.FormsのPageNavigationServiceを継承してカスタム、という形にしたかったのですが期待する動作をさせるためにはどうしても無理であったためPrism.Formsのコードを持ってきてカスタマイズを行いました。
変更点は先ほど作成したTabbedMasterDetailPage用の処理関数ProcessNavigationForTabbedPageを追加し呼び出されるようにしたことです。
この関数の中身自体はTabbedPage用の関数とほぼ同じです。
先ほど説明した2つのアプローチの1番であればNavigationServiceをカスタマイズする必要はなく、そういういみでもアプローチ1は理想形と言えます。
Appクラス
Prism.Forms_NavigationSample/App.cs at master · yuka1984/Prism.Forms_NavigationSample · GitHub
Appクラスはこのように実装しています。
あまり良い実装ではないです。
RegisterTypesにてランダムにNavigationPageを登録しています。
この時に名前をRootPageという名前で登録することでawait NavigationService.NavigateAsync("RootPage/Content2");というように指定することができます。
ここまでの実装によりMasterDetailな見た目だけどロジック的にはTabbedPageと共通で実装することができました。
終わりに
Xamarin.FormsはMVVMパターン・・・みたいに言われることがあるのですがなんか違う気がしてきています。
というよりMVVMってなんだろう。MVVMとはいったいなんだろう・・・・なんなんだぁ(/・ω・)/
MVVMを考えるときにNavigationを入れちゃだめだ(;^ω^)
clean architecture Reactive・・・・
:(´◦ω◦`):