-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
977 lines (642 loc) · 241 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Medic Consulting</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description">
<meta property="og:type" content="website">
<meta property="og:title" content="Medic Consulting">
<meta property="og:url" content="http://www.medic-consulting.com/index.html">
<meta property="og:site_name" content="Medic Consulting">
<meta property="og:description">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Medic Consulting">
<meta name="twitter:description">
<link rel="icon" href="/favicon.png">
<link href="/webfonts/ptserif/main.css" rel='stylesheet' type='text/css'>
<link href="/webfonts/source-code-pro/main.css" rel="stylesheet" type="text/css">
<link href="http://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
<!-- Google Analytics -->
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-56342083-1', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->
</head>
<body>
<div id="container">
<header id="header">
<div id="header-outer" class="outer">
<div id="header-inner" class="inner">
<a id="main-nav-toggle" class="nav-icon" href="javascript:;"></a>
<a id="logo" class="logo" href="/"></a>
<nav id="main-nav">
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
<a class="main-nav-link" href="/tags">Tags</a>
<a class="main-nav-link" href="/about">About</a>
<a id="social-nav" href="https://www.linkedin.com/in/andrejmedic"><i class="fa fa-linkedin-square" aria-hidden="true"></i></a>
<a id="social-nav" href="https://github.com/medand"><i class="fa fa-github" aria-hidden="true"></i></a>
<a id="social-nav" href="https://twitter.com/AndrejMedic"><i class="fa fa-twitter" aria-hidden="true"></i></a>
<a id="social-nav" href="/atom.xml"><i class="fa fa-rss" aria-hidden="true"></i></a>
</nav>
<nav id="sub-nav">
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" results="0" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://www.medic-consulting.com"></form>
</div>
</nav>
</div>
</div>
</header>
<section id="main" class="outer">
<article id="post-Monitoring-Azure-Service-Fabric-Microservices-with-Azure-Monitor" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/06/12/Monitoring-Azure-Service-Fabric-Microservices-with-Azure-Monitor/">Monitoring Azure Service Fabric Microservices with Azure Monitor</a>
</h1>
</header>
<div class="article-meta">
<a href="/2019/06/12/Monitoring-Azure-Service-Fabric-Microservices-with-Azure-Monitor/" class="article-date">
<time datetime="2019-06-12T11:13:00.000Z" itemprop="datePublished">2019-06-12</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>By their very nature distributed platforms, applications and services running in the cloud are comprised of many moving parts. In this post we’ll see just how simple it is to add powerful & unified monitoring to your Azure Service Fabric clusters by integrating with the Azure Monitor service (formerly known as Azure Log Analytics). We’ll also touch upon a few Azure Service Fabric / Azure Monitor best practices from the field. </p>
<p>To start off, Azure Monitor provides a single, cost effective & integrated experience for monitoring Azure resources and hybrid environments. It helps with maximizing operational availability, performance & resource utilization of your VMs and containers by collecting, analyzing and acting on both platform and application level telemetry. </p>
<p>Data collected by Azure Monitor fits into two groups, metrics and logs:</p>
<blockquote>
<p><em><strong>Metrics:</strong> are numerical values that describe some aspect of a system at a particular point in time. They are lightweight and capable of supporting near real-time scenarios.</em></p>
<p><em><strong>Logs:</strong> contain different kinds of data organized into records with different sets of properties for each type. Telemetry such as events and traces are stored as logs in addition to performance data so that it can all be combined for analysis.</em></p>
</blockquote>
<h3 id="Log-Analytics-agent"><a href="#Log-Analytics-agent" class="headerlink" title="Log Analytics agent"></a>Log Analytics agent</h3><p>For advanced Azure Service Fabric monitoring scenarios we’ll forgo the usual recommendation of using the Azure Diagnostics extension (commonly referred to as the Windows Azure Diagnostic (WAD) or Linux Azure Diagnostic (LAD) extension) and instead opt to leverage the more flexible Log Analytics agent. I like to think of the Log Analytics agent as just another microservice which runs on all Service Fabric nodes but first we need to make it part of the Virtual Machine Scale Set (VMSS). </p>
<blockquote>
<p><em>The Log Analytics agent was developed for comprehensive management across on-premises physical and virtual machines, containers and VMs hosted in other clouds. The Windows and Linux agents connect to a Log Analytics workspace in Azure Monitor to collect both monitoring solution-based data as well as custom data sources that you configure.</em></p>
</blockquote>
<h3 id="Adding-the-Log-Analytics-agent-to-the-Virtual-Machine-Scale-Set-VMSS"><a href="#Adding-the-Log-Analytics-agent-to-the-Virtual-Machine-Scale-Set-VMSS" class="headerlink" title="Adding the Log Analytics agent to the Virtual Machine Scale Set (VMSS)"></a>Adding the Log Analytics agent to the Virtual Machine Scale Set (VMSS)</h3><p>I’ve assumed you have an Azure Monitor Log Analytics workspace already set-up, but if you don’t then head over to <a href="https://docs.microsoft.com/en-us/azure/azure-monitor/learn/quick-create-workspace" target="_blank" rel="external">Create a Log Analytics workspace in the Azure portal</a>. </p>
<p>The easiest way to add the Log Analytics agent to the underlying Service Fabric Virtual Machine Scale Set (VMSS) is to use the Cloud Shell or Azure CLI. The following official documentation from the Azure Service Fabric team does a great job of explaining the process step by step: <a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-oms-agent#add-the-agent-extension-via-azure-cli" target="_blank" rel="external">Add the agent extension via Azure CLI</a>.</p>
<p>Note that the Log Analytics agent can also be added directly to an Azure Service Fabric cluster Resource Manager template in case of standing-up new clusters, thus configured with Azure Monitor integration from the get-go.</p>
<p>For bonus points, if you prefer PowerShell and having tested it, a great community contribution from Nilay Parikh performs the task just as well: <a href="https://github.com/nilayparikh/AzureScripts/tree/master/Add-OMSAgentVmssExtension" target="_blank" rel="external">Add-OMSAgentVmssExtension</a>:</p>
<blockquote>
<p><code>.\Add-OMSAgentVmssExtension.ps1 -ResourceGroupLocation "location" -ResourceGroupName "yourresourcegroup" -WorkspaceName "omsworkspacename" -VMScaleSetName "scalesetname" -AutoUpgradeMinorVersion</code></p>
</blockquote>
<p>Following the above steps, the Log Analytics agent is now part of your Service Fabric Virtual Machine Scale Set (VMSS), with any running nodes upgraded, a process that usually takes 20 minutes. Note that the upgrade is performed in a rolling manner, and if your durability level supports it, with zero downtime to your application. Any new nodes created as a result of cluster scaling operations will likewise have the Log Analytics agent automatically deployed.</p>
<h3 id="Azure-Service-Fabric-Performance-Counters"><a href="#Azure-Service-Fabric-Performance-Counters" class="headerlink" title="Azure Service Fabric Performance Counters"></a>Azure Service Fabric Performance Counters</h3><p>With the Log Analytics agent successfully running on each Service Fabric node, we are now ready to start collecting metrics and logs. Luckily for us the Log Analytics agent comes with an understated feature, a built in control plane, meaning we can configure at will which metrics and logs we wish to collect, and at what interval via the Azure Portal. </p>
<p>To do so, in the Azure portal go to the resource group in which you created the Service Fabric Analytics solution. Select the name of your Analytics Workspace:</p>
<ol>
<li>Click Advanced Settings.</li>
<li>Click Data, then click Windows or Linux Performance Counters.</li>
<li>Select from default / custom performance counters.</li>
<li>Click Save, then click OK.</li>
</ol>
<p>Refer to official documentation for a full list of <a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-event-generation-perf" target="_blank" rel="external">recommended Azure Service Fabric cluster performance counters</a>. In addition Service Fabric generates a substantial amount of custom performance counters:</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-diagnostics" target="_blank" rel="external">Stateful Reliable Services counters</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-serviceremoting-diagnostics" target="_blank" rel="external">Reliable Service Remoting counters</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-actors-diagnostics" target="_blank" rel="external">Reliable Actors counters</a></li>
</ul>
<blockquote>
<p><em><strong>Note:</strong> The number of stateful service partitions has a direct correlation to the volume of metrics collected per service. For example an increase in partitions from 5 to 25 would yield a similar jump in volume of metrics and cost of Azure Monitor. As a result, carefully consider and tune the interval for collection of high volume Azure Service Fabric custom performance counters.</em></p>
</blockquote>
<h3 id="Analytics-amp-diagnostics"><a href="#Analytics-amp-diagnostics" class="headerlink" title="Analytics & diagnostics"></a>Analytics & diagnostics</h3><p>Having configured which performance counters the Log Analytics agent collects, within seconds the data is available in the Azure Monitor workspace for alerting, analytics & diagnostic purposes. For example, we can now visualize the number of Reliable Service new write transactions created per second across the cluster. Select the name of your Analytics Workspace:</p>
<ol>
<li>Click Logs.</li>
<li>Execute the below Kusto query & click CHART:</li>
</ol>
<blockquote>
<p><code>Perf
| where ObjectName == "Service Fabric Transactional Replicator" and CounterName == "Begin Txn Operations/sec"
| summarize TransactionOperationsPerSecond = avg(CounterValue) by bin(TimeGenerated, 1m)</code></p>
</blockquote>
<p><img src="/images/008_KustoGraph.jpg" alt="Begin Txn Operations/sec"></p>
<h3 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h3><p>In this post, we’ve learned how simple it is to add powerful & unified platform monitoring to your Azure Service Fabric clusters by integrating with the Azure Monitor service. We deployed & configured the Log Analytics agent using the recommended default and custom performance counters, and hopefully highlighted the correlation of stateful service partitions to Azure Monitor cost. Lastly we crafted our very first Log Analytics Kusto query! In future posts we’ll expand our focus to also cover application level telemetry.</p>
<hr>
<h4 id="References"><a href="#References" class="headerlink" title="References"></a>References</h4><ol>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-oms-agent" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-oms-agent</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/azure-monitor/learn/quick-create-workspace" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/azure-monitor/learn/quick-create-workspace</a></li>
<li><a href="https://github.com/nilayparikh/AzureScripts/tree/master/Add-OMSAgentVmssExtension" target="_blank" rel="external">https://github.com/nilayparikh/AzureScripts/tree/master/Add-OMSAgentVmssExtension</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-event-generation-perf" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-event-generation-perf</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-diagnostics" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-diagnostics</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-serviceremoting-diagnostics" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-serviceremoting-diagnostics</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-actors-diagnostics" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-actors-diagnostics</a></li>
</ol>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Azure-Monitor/">Azure Monitor</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Log-Analytics/">Log Analytics</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Upgrade-your-Net-Core-Service-Fabric-Microservices-from-VS-2015-to-VS-2017" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2017/06/07/Upgrade-your-Net-Core-Service-Fabric-Microservices-from-VS-2015-to-VS-2017/">Upgrade your .Net Core Service Fabric Microservices from VS 2015 to VS 2017</a>
</h1>
</header>
<div class="article-meta">
<a href="/2017/06/07/Upgrade-your-Net-Core-Service-Fabric-Microservices-from-VS-2015-to-VS-2017/" class="article-date">
<time datetime="2017-06-07T12:56:00.000Z" itemprop="datePublished">2017-06-07</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Service Fabric projects have evolved at what feels like a cracking pace, along with the .Net Core platform and tooling, and with the recent release of Visual Studio 2017 no doubt you are considering the productivity merits of upgrading (container support). For Service Fabric projects designed in Visual Studio 2015 and using the .Net Core .xproj/project.json structures now deprecated in Visual Studio 2017, the automatic upgrade process may result in only partial conversion success.</p>
<p>In this article we’ll take a look at the issues encountered while upgrading a .Net Core Service Fabric solution containing 77 .xproj/project.json projects to Visual Studio 2017. </p>
<h3 id="From-Net-Core-Visual-Studio-2015-xproj-project-json-to-Visual-Studio-2017-csproj"><a href="#From-Net-Core-Visual-Studio-2015-xproj-project-json-to-Visual-Studio-2017-csproj" class="headerlink" title="From .Net Core Visual Studio 2015 .xproj/project.json to Visual Studio 2017 .csproj"></a>From .Net Core Visual Studio 2015 .xproj/project.json to Visual Studio 2017 .csproj</h3><p>To begin, let’s take a look at a simplified example of a stateful .Net Core microservice defined with the following project.json (VS 2015) structure:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"title"</span>: <span class="string">"Acme.Service.Auction"</span>,</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"Acme.Service.Auction"</span>,</span><br><span class="line"> <span class="attr">"version"</span>: <span class="string">"1.0.0-*"</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">"buildOptions"</span>: {</span><br><span class="line"> <span class="attr">"emitEntryPoint"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"preserveCompilationContext"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"compile"</span>: {</span><br><span class="line"> <span class="attr">"exclude"</span>: [</span><br><span class="line"> <span class="string">"PackageRoot"</span></span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"dependencies"</span>: {</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric"</span>: <span class="string">"5.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric.Services"</span>: <span class="string">"2.1.150"</span>,</span><br><span class="line"> <span class="attr">"EnterpriseLibrary.SemanticLogging"</span>: <span class="string">"2.0.1406.1"</span></span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"frameworks"</span>: {</span><br><span class="line"> <span class="attr">"net46"</span>: {}</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"runtimes"</span>: {</span><br><span class="line"> <span class="attr">"win7-x64"</span>: {}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Once the automatic Visual Studio 2017 conversion completes, you’ll end up with a .csproj file similar to the below:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">PropertyGroup</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Description</span>></span>Acme.Service.Auction<span class="tag"></<span class="name">Description</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">AssemblyTitle</span>></span>Acme.Service.Auction<span class="tag"></<span class="name">AssemblyTitle</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">TargetFramework</span>></span>net46<span class="tag"></<span class="name">TargetFramework</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">PreserveCompilationContext</span>></span>true<span class="tag"></<span class="name">PreserveCompilationContext</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">AssemblyName</span>></span>Acme.Service.Auction<span class="tag"></<span class="name">AssemblyName</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">OutputType</span>></span>Exe<span class="tag"></<span class="name">OutputType</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">PackageId</span>></span>Acme.Service.Auction<span class="tag"></<span class="name">PackageId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">RuntimeIdentifiers</span>></span>win7-x64<span class="tag"></<span class="name">RuntimeIdentifiers</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyTitleAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyTitleAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyDescriptionAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyDescriptionAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyConfigurationAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyConfigurationAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyCompanyAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyCompanyAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyProductAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyProductAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyCopyrightAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyCopyrightAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyVersionAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyVersionAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">GenerateAssemblyFileVersionAttribute</span>></span>false<span class="tag"></<span class="name">GenerateAssemblyFileVersionAttribute</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">IsServiceFabricServiceProject</span>></span>True<span class="tag"></<span class="name">IsServiceFabricServiceProject</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">PropertyGroup</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">ItemGroup</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Compile</span> <span class="attr">Remove</span>=<span class="string">"PackageRoot\**\*"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemGroup</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">ItemGroup</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.ServiceFabric"</span> <span class="attr">Version</span>=<span class="string">"5.1.150"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.ServiceFabric.Services"</span> <span class="attr">Version</span>=<span class="string">"2.1.150"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"EnterpriseLibrary.SemanticLogging"</span> <span class="attr">Version</span>=<span class="string">"2.0.1406.1"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemGroup</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">ItemGroup</span> <span class="attr">Condition</span>=<span class="string">" '$(TargetFramework)' == 'net46' "</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Reference</span> <span class="attr">Include</span>=<span class="string">"System"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">Reference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.CSharp"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemGroup</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">Project</span>></span></span><br></pre></td></tr></table></figure>
<h3 id="Processor-architecture-mismatch-warnings"><a href="#Processor-architecture-mismatch-warnings" class="headerlink" title="Processor architecture mismatch warnings"></a>Processor architecture mismatch warnings</h3><p>If you compile the above project, in the build output window you may notice processor architecture mismatch warnings, for example:</p>
<p><code>1>C:\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets(1964,5): warning MSB3270: There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "C:\Users\Admin\.nuget\packages\microsoft.servicefabric.services\2.1.150\lib\net45\Microsoft.ServiceFabric.Services.dll", "AMD64". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project.</code></p>
<p>To fix these and similar processor architecture mismatch warnings, replace:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">RuntimeIdentifiers</span>></span>win7-x64<span class="tag"></<span class="name">RuntimeIdentifiers</span>></span></span><br></pre></td></tr></table></figure>
<p>With this (there is no ending s):</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">RuntimeIdentifier</span>></span>win7-x64<span class="tag"></<span class="name">RuntimeIdentifier</span>></span></span><br></pre></td></tr></table></figure>
<h3 id="Packaging-and-Publishing…-not-so-fast"><a href="#Packaging-and-Publishing…-not-so-fast" class="headerlink" title="Packaging and Publishing… not so fast!"></a>Packaging and Publishing… not so fast!</h3><p>So the converted microservice now compiles without any warnings, what’s all the fuss about… well if you now attempt to package and publish this microservice to Service Fabric, it fails with a message similar to the below:</p>
<p><code>C:\AcmeAuctions\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets(248,5): warning MSB3026: Could not copy "C:\AcmeAuctions\src\Acme.Service.Auction\bin\x64\Debug\net46\win7-x64\Acme.Service.Auction.runtimeconfig.json" to "C:\AcmeAuctions\pkg\Debug\Acme.Service.AuctionPkg\Code". Beginning retry 1 in 1000ms. Could not find a part of the path 'C:\AcmeAuctions\pkg\Debug\Acme.Service.AuctionPkg\Code'.</code></p>
<p>Cross checking various existing github and stackoverflow issues, the current Service Fabric SDK for VS 2017 and msbuild tooling appear not support .Net Core projects for Actor, Stateful and Stateless services defined with Microsoft.NET.Sdk. To clarify, the tooling supports Stateful and Stateless ASP.Net Core service projects only, however I prefer all projects to be in .Net Core, not just my ASP.Net microservices. Hence I replace this:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk"</span>></span></span><br></pre></td></tr></table></figure>
<p>With the below; as I expect the above scenario to be resolved in the near future with a SDK and tooling update. I’ve simply switched to a supported Service Fabric and VS 2017 template scenario which is to define all microservice .csproj files using Microsoft.NET.Sdk.Web:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk.Web"</span>></span></span><br></pre></td></tr></table></figure>
<p>With this simple change your Service Fabric microservies will support .Net Core Actor, Stateful and Stateless VS 2017 projects and will package and publish normally. Note that in Visual Studio 2017 the project icon will change to a web project, and you may optionally want to git ignore and exclude launchSettings.json files, however given the non-intrusive workaround I believe it’s well worth it. To remove the launchSettings.json file from your project, modify the ItemGroup to:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ItemGroup</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Compile</span> <span class="attr">Remove</span>=<span class="string">"PackageRoot\**\*"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">Content</span> <span class="attr">Remove</span>=<span class="string">"Properties\launchSettings.json"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">ItemGroup</span>></span></span><br></pre></td></tr></table></figure>
<h3 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h3><p>We’ve looked at some simple changes you can make to your converted and upgraded Service Fabric project files. The changes allow you to write your Actor, Stateful and Stateless services in .Net Core while taking advantage of the great new productivity gains (Azure integration, Docker support etc.) offered by Visual Studio 2017 and Service Fabric!</p>
<p>In our next article we’ll continue the upgrade journey by walking through a few DevOps limitations encountered while reconfiguring a Service Fabric Visual Studio Team Services CI/CD pipeline.</p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/VS-2017/">VS 2017</a></li></ul>
</footer>
</div>
</article>
<article id="post-Asp-Net-Core-DataProtection-for-Service-Fabric-with-Kestrel-WebListener" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2017/01/19/Asp-Net-Core-DataProtection-for-Service-Fabric-with-Kestrel-WebListener/">ASP.NET Core DataProtection for Service Fabric with Kestrel & WebListener</a>
</h1>
</header>
<div class="article-meta">
<a href="/2017/01/19/Asp-Net-Core-DataProtection-for-Service-Fabric-with-Kestrel-WebListener/" class="article-date">
<time datetime="2017-01-18T13:13:00.000Z" itemprop="datePublished">2017-01-19</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>In ASP.NET 1.x - 4.x, if you deployed your application to a Web farm, you had to ensure that the configuration files on each server shared the same value for validationKey and decryptionKey, which were used for hashing and decryption respectively. In ASP.NET Core this is accomplished via the data protection stack which was designed to address many of the shortcomings of the old cryptographic stack. The new API provides a simple, easy to use mechanism for data encryption, decryption, key management and rotation. The data protection system ships with several in-box key storage providers; File system, Registry, AzureStorage and Redis. </p>
<p>Since we are working with low-latency microservices at massive scale via Azure Service Fabric, in this blog post we’ll describe an approach to create a custom ASP.NET Core data protection key repository using Service Fabric’s built in Reliable Collections, which are Replicated, Persisted, Asynchronous and Transactional. </p>
<p>Previous readers will note we’ve covered how to integrate ASP.Net Core and Kestrel into Service Fabric, moreover how to create Service Fabric microservices in the new .Net Core xproj structure (soon to be superseded with VS 2017), therefore we’ll jump straight into building the AspNetCore.DataProtection.ServiceFabric microservice (warning this post is code heavy). To test everything out we’ll create a sample ASP.Net Core Web API microservice and finally for completeness integrate WebListener, a Windows only web server.</p>
<p>To begin, we create a new stateful Service Fabric microservice called DataProtectionService: </p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Data;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Data.Collections;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Communication.Runtime;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Remoting.Runtime;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Runtime;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Fabric;</span><br><span class="line"><span class="keyword">using</span> System.Threading;</span><br><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> System.Xml.Linq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AspNetCore.DataProtection.ServiceFabric</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> <span class="title">DataProtectionService</span> : <span class="title">StatefulService</span>, <span class="title">IDataProtectionService</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">DataProtectionService</span>(<span class="params">StatefulServiceContext context, IReliableStateManager stateManager</span>) : <span class="title">base</span>(<span class="params">context, stateManager <span class="keyword">as</span> IReliableStateManagerReplica</span>)</span><br><span class="line"> </span>{</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> IEnumerable<ServiceReplicaListener> <span class="title">CreateServiceReplicaListeners</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span>[]</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">new</span> ServiceReplicaListener(context => <span class="keyword">this</span>.CreateServiceRemotingListener(context))</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">async</span> Task<List<XElement>> GetAllDataProtectionElements()</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> elements = <span class="keyword">new</span> List<XElement>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> dictionary = <span class="keyword">await</span> <span class="keyword">this</span>.StateManager.GetOrAddAsync<IReliableDictionary<Guid, XElement>>(<span class="string">"AspNetCore.DataProtection"</span>);</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> tx = <span class="keyword">this</span>.StateManager.CreateTransaction())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> enumerable = <span class="keyword">await</span> dictionary.CreateEnumerableAsync(tx);</span><br><span class="line"> <span class="keyword">var</span> enumerator = enumerable.GetAsyncEnumerator();</span><br><span class="line"> <span class="keyword">var</span> token = <span class="keyword">new</span> CancellationToken();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">await</span> enumerator.MoveNextAsync(token))</span><br><span class="line"> {</span><br><span class="line"> elements.Add(enumerator.Current.Value);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> elements;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<XElement> <span class="title">AddDataProtectionElement</span>(<span class="params">XElement element</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> Guid id = Guid.Parse(element.Attribute(<span class="string">"id"</span>).Value);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> dictionary = <span class="keyword">await</span> <span class="keyword">this</span>.StateManager.GetOrAddAsync<IReliableDictionary<Guid, XElement>>(<span class="string">"AspNetCore.DataProtection"</span>);</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> tx = <span class="keyword">this</span>.StateManager.CreateTransaction())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> result = <span class="keyword">await</span> dictionary.GetOrAddAsync(tx, id, element);</span><br><span class="line"> <span class="keyword">await</span> tx.CommitAsync();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Congratulations you’ve just implemented a custom key storage provider using a Service Fabric Reliable Dictionary! To integrate with ASP.Net Core Data Protection API we need to also create a ServiceFabricXmlRepository class which implements IXmlRepository. In a new stateless microservice called ServiceFabric.DataProtection.Web create ServiceFabricXmlRepository:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> AspNetCore.DataProtection.ServiceFabric;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.DataProtection.Repositories;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Client;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Remoting.Client;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Xml.Linq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ServiceFabric.DataProtection.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ServiceFabricXmlRepository</span> : <span class="title">IXmlRepository</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> IReadOnlyCollection<XElement> <span class="title">GetAllElements</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> proxy = ServiceProxy.Create<IDataProtectionService>(<span class="keyword">new</span> Uri(<span class="string">"fabric:/ServiceFabric.DataProtection/DataProtectionService"</span>), <span class="keyword">new</span> ServicePartitionKey());</span><br><span class="line"> <span class="keyword">return</span> proxy.GetAllDataProtectionElements().Result.AsReadOnly();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">StoreElement</span>(<span class="params">XElement element, <span class="keyword">string</span> friendlyName</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (element == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException(<span class="keyword">nameof</span>(element));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> proxy = ServiceProxy.Create<IDataProtectionService>(<span class="keyword">new</span> Uri(<span class="string">"fabric:/ServiceFabric.DataProtection/DataProtectionService"</span>), <span class="keyword">new</span> ServicePartitionKey());</span><br><span class="line"> proxy.AddDataProtectionElement(element).Wait();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>To easily bootstrap our custom ServiceFabricXmlRepository into ASP.Net Core on start-up, create the following DataProtectionBuilderExtensions class:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.DataProtection;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.DataProtection.Repositories;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ServiceFabric.DataProtection.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">DataProtectionBuilderExtensions</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IDataProtectionBuilder <span class="title">PersistKeysToServiceFabric</span>(<span class="params"><span class="keyword">this</span> IDataProtectionBuilder builder</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (builder == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException(<span class="keyword">nameof</span>(builder));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> builder.Use(ServiceDescriptor.Singleton<IXmlRepository>(services => <span class="keyword">new</span> ServiceFabricXmlRepository()));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IDataProtectionBuilder <span class="title">Use</span>(<span class="params"><span class="keyword">this</span> IDataProtectionBuilder builder, ServiceDescriptor descriptor</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (builder == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException(<span class="keyword">nameof</span>(builder));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (descriptor == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException(<span class="keyword">nameof</span>(descriptor));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = builder.Services.Count - <span class="number">1</span>; i >= <span class="number">0</span>; i--)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (builder.Services[i]?.ServiceType == descriptor.ServiceType)</span><br><span class="line"> {</span><br><span class="line"> builder.Services.RemoveAt(i);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> builder.Services.Add(descriptor);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> builder;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Building upon previous articles detailing how to integrate Kestrel and Service Fabric, we extend WebHostBuilderHelper to also support the WebListener webserver:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Net.Http.Server;</span><br><span class="line"><span class="keyword">using</span> System.Fabric;</span><br><span class="line"><span class="keyword">using</span> System.IO;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ServiceFabric.DataProtection.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">internal</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">WebHostBuilderHelper</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IWebHost <span class="title">GetServiceFabricWebHost</span>(<span class="params">ServerType serverType</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> endpoint = FabricRuntime.GetActivationContext().GetEndpoint(<span class="string">"ServiceEndpoint"</span>);</span><br><span class="line"> <span class="keyword">string</span> serverUrl = <span class="string">$"<span class="subst">{endpoint.Protocol}</span>://<span class="subst">{FabricRuntime.GetNodeContext().IPAddressOrFQDN}</span>:<span class="subst">{endpoint.Port}</span>"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> GetWebHost(endpoint.Protocol.ToString(), endpoint.Port.ToString(), serverType);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IWebHost <span class="title">GetWebHost</span>(<span class="params"><span class="keyword">string</span> protocol, <span class="keyword">string</span> port, ServerType serverType</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (serverType)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> ServerType.WebListener:</span><br><span class="line"> {</span><br><span class="line"> IWebHostBuilder webHostBuilder = <span class="keyword">new</span> WebHostBuilder()</span><br><span class="line"> .UseWebListener(options =></span><br><span class="line"> {</span><br><span class="line"> options.ListenerSettings.Authentication.Schemes = AuthenticationSchemes.None;</span><br><span class="line"> options.ListenerSettings.Authentication.AllowAnonymous = <span class="literal">true</span>;</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ConfigureWebHostBuilder(webHostBuilder, protocol, port);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> ServerType.Kestrel:</span><br><span class="line"> {</span><br><span class="line"> IWebHostBuilder webHostBuilder = <span class="keyword">new</span> WebHostBuilder();</span><br><span class="line"> webHostBuilder.UseKestrel();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ConfigureWebHostBuilder(webHostBuilder, protocol, port);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">static</span> IWebHost <span class="title">ConfigureWebHostBuilder</span>(<span class="params">IWebHostBuilder webHostBuilder, <span class="keyword">string</span> protocol, <span class="keyword">string</span> port</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">return</span> webHostBuilder</span><br><span class="line"> .UseContentRoot(Directory.GetCurrentDirectory())</span><br><span class="line"> .UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), <span class="string">"wwwroot"</span>))</span><br><span class="line"> .UseStartup<Startup>()</span><br><span class="line"> .UseUrls(<span class="string">$"<span class="subst">{protocol}</span>://+:<span class="subst">{port}</span>"</span>)</span><br><span class="line"> .Build();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">enum</span> ServerType</span><br><span class="line"> {</span><br><span class="line"> Kestrel,</span><br><span class="line"> WebListener</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Your Web microservice should look something like:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Communication.AspNetCore;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Communication.Runtime;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Runtime;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Fabric;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ServiceFabric.DataProtection.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> <span class="title">WebService</span> : <span class="title">StatelessService</span></span><br><span class="line"> {</span><br><span class="line"> ServerType _serverType;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">WebService</span>(<span class="params">StatelessServiceContext context, ServerType serverType</span>)</span><br><span class="line"> : <span class="title">base</span>(<span class="params">context</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> _serverType = serverType;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> IEnumerable<ServiceInstanceListener> <span class="title">CreateServiceInstanceListeners</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ServiceInstanceListener[]</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">new</span> ServiceInstanceListener(serviceContext =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">switch</span> (_serverType)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> ServerType.WebListener :</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> WebListenerCommunicationListener(serviceContext, <span class="string">"ServiceEndpoint"</span>, url =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> WebHostBuilderHelper.GetServiceFabricWebHost(_serverType);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> ServerType.Kestrel:</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> KestrelCommunicationListener(serviceContext, <span class="string">"ServiceEndpoint"</span>, url =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> WebHostBuilderHelper.GetServiceFabricWebHost(_serverType);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> }) </span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Next modify Program.cs with below code:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> CommandLine;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Runtime;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Threading;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ServiceFabric.DataProtection.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">internal</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> parser = <span class="keyword">new</span> Parser(with => </span><br><span class="line"> {</span><br><span class="line"> with.EnableDashDash = <span class="literal">true</span>;</span><br><span class="line"> with.HelpWriter = Console.Out;</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> result = parser.ParseArguments<Options>(args);</span><br><span class="line"></span><br><span class="line"> result.MapResult(options =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">switch</span> (options.Host.ToLower())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"servicefabric-weblistener"</span>:</span><br><span class="line"> {</span><br><span class="line"> ServiceRuntime.RegisterServiceAsync(<span class="string">"WebServiceType"</span>, context => <span class="keyword">new</span> WebService(context, ServerType.WebListener)).GetAwaiter().GetResult();</span><br><span class="line"> Thread.Sleep(Timeout.Infinite);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"servicefabric-kestrel"</span>:</span><br><span class="line"> {</span><br><span class="line"> ServiceRuntime.RegisterServiceAsync(<span class="string">"WebServiceType"</span>, context => <span class="keyword">new</span> WebService(context, ServerType.Kestrel)).GetAwaiter().GetResult();</span><br><span class="line"> Thread.Sleep(Timeout.Infinite);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"weblistener"</span>:</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> host = WebHostBuilderHelper.GetWebHost(options.Protocol, options.Port, ServerType.WebListener))</span><br><span class="line"> {</span><br><span class="line"> host.Run();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"kestrel"</span>:</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> host = WebHostBuilderHelper.GetWebHost(options.Protocol, options.Port, ServerType.Kestrel))</span><br><span class="line"> {</span><br><span class="line"> host.Run();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> },</span><br><span class="line"> errors =></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> <span class="title">Options</span></span><br><span class="line"> {</span><br><span class="line"> [Option(Default = <span class="string">"weblistener"</span>, HelpText = <span class="string">"Host - Options [weblistener] or [kestrel] or [servicefabric-weblistener] or [servicefabric-kestrel]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Host { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [Option(Default = <span class="string">"http"</span>, HelpText = <span class="string">"Protocol - Options [http] or [https]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Protocol { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [Option(Default = <span class="string">"localhost"</span>, HelpText = <span class="string">"IP Address or Uri - Example [localhost] or [127.0.0.1]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> IpAddressOrFQDN { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [Option(Default = <span class="string">"5000"</span>, HelpText = <span class="string">"Port - Example [80] or [5000]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Port { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>And finally PersistKeysToServiceFabric needs to be added to Startup.cs as this will instruct the ASP.NET Core data protection stack to use our custom AspNetCore.DataProtection.ServiceFabric key repository:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Builder;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.DataProtection;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Configuration;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Logging;</span><br><span class="line"><span class="keyword">using</span> Swashbuckle.AspNetCore.Swagger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ServiceFabric.DataProtection.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Startup</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Startup</span>(<span class="params">IHostingEnvironment env</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> builder = <span class="keyword">new</span> ConfigurationBuilder()</span><br><span class="line"> .SetBasePath(env.ContentRootPath)</span><br><span class="line"> .AddJsonFile(<span class="string">"appsettings.json"</span>, optional: <span class="literal">true</span>, reloadOnChange: <span class="literal">true</span>)</span><br><span class="line"> .AddJsonFile(<span class="string">$"appsettings.<span class="subst">{env.EnvironmentName}</span>.json"</span>, optional: <span class="literal">true</span>)</span><br><span class="line"> .AddEnvironmentVariables();</span><br><span class="line"> Configuration = builder.Build();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> IConfigurationRoot Configuration { <span class="keyword">get</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// This method gets called by the runtime. Use this method to add services to the container.</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="comment">// Add framework services.</span></span><br><span class="line"> services.AddMvc();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Add Service Fabric DataProtection</span></span><br><span class="line"> services.AddDataProtection()</span><br><span class="line"> .SetApplicationName(<span class="string">"ServiceFabric-DataProtection-Web"</span>)</span><br><span class="line"> .PersistKeysToServiceFabric();</span><br><span class="line"></span><br><span class="line"> services.AddSwaggerGen(c =></span><br><span class="line"> {</span><br><span class="line"> c.SwaggerDoc(<span class="string">"v1"</span>, <span class="keyword">new</span> Info { Title = <span class="string">"AspNetCore.DataProtection.ServiceFabric API"</span>, Version = <span class="string">"v1"</span> });</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> app.UseMvc();</span><br><span class="line"> app.UseSwaggerUi(c =></span><br><span class="line"> {</span><br><span class="line"> c.SwaggerEndpoint(<span class="string">"/swagger/v1/swagger.json"</span>, <span class="string">"AspNetCore.DataProtection.ServiceFabric API v1"</span>);</span><br><span class="line"> });</span><br><span class="line"> app.UseSwagger();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>All that is now left to do is within your .Net Core Web Application PackageRoot, edit the ServiceManifest.xml CodePackage so that we tell Web.exe to “host” within Service Fabric using WebListener:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">CodePackage</span> <span class="attr">Name</span>=<span class="string">"Code"</span> <span class="attr">Version</span>=<span class="string">"1.0.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">EntryPoint</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ExeHost</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Program</span>></span>ServiceFabric.DataProtection.Web.exe<span class="tag"></<span class="name">Program</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Arguments</span>></span>--host servicefabric-weblistener<span class="tag"></<span class="name">Arguments</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">WorkingFolder</span>></span>CodePackage<span class="tag"></<span class="name">WorkingFolder</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ConsoleRedirection</span> <span class="attr">FileRetentionCount</span>=<span class="string">"5"</span> <span class="attr">FileMaxSizeInKb</span>=<span class="string">"2048"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">ExeHost</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">EntryPoint</span>></span></span><br><span class="line"><span class="tag"></<span class="name">CodePackage</span>></span></span><br></pre></td></tr></table></figure>
<p>At an administrative command prompt you’ll need to issue the below command to create the correct Url ACL for port 80 (please refer to the WebListener references section below for detailed instructions):</p>
<p><code>netsh http add urlacl url=http://+:80/ user=Users</code></p>
<p>Upon successful deployment to a multi-node cluster, use Swagger and the Protect/Unprotect APIs to test that all nodes have access to the same data protection keys:</p>
<p><img src="/images/007_AspNetCoreDataProtectionServiceFabricSwaggerAPI.jpg" alt="ASP.Net Core DataProtection ServiceFabric Swagger API"></p>
<p><code>Note, as we've created a custom ASP.NET Core data protection key repository, the data protection system will deregister the default key encryption at rest mechanism that the heuristic provided, so keys will no longer be encrypted at rest. It is strongly recommended that you additionally <a href="https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-encryption-at-rest#data-protection-implementation-key-encryption-at-rest-providers" target="_blank" rel="external">specify an explicit key encryption mechanism</a> for production applications.</code></p>
<hr>
<h4 id="References"><a href="#References" class="headerlink" title="References"></a>References</h4><ol>
<li><a href="https://msdn.microsoft.com/en-us/library/ff649308.aspx#paght000007_webfarmdeploymentconsiderations" target="_blank" rel="external">https://msdn.microsoft.com/en-us/library/ff649308.aspx#paght000007_webfarmdeploymentconsiderations</a></li>
<li><a href="https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/introduction" target="_blank" rel="external">https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/introduction</a></li>
<li><a href="https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-storage-providers" target="_blank" rel="external">https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-storage-providers</a></li>
<li><a href="https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections" target="_blank" rel="external">https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections</a></li>
<li><a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/weblistener" target="_blank" rel="external">https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/weblistener</a></li>
<li><a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel" target="_blank" rel="external">https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel</a></li>
</ol>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Security/">Security</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Application-Insights-and-Semantic-Logging-for-Service-Fabric-Microservices" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/08/12/Application-Insights-and-Semantic-Logging-for-Service-Fabric-Microservices/">Application Insights & Semantic Logging for Service Fabric Microservices</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/08/12/Application-Insights-and-Semantic-Logging-for-Service-Fabric-Microservices/" class="article-date">
<time datetime="2016-08-12T12:01:00.000Z" itemprop="datePublished">2016-08-12</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Borrowing heavily from MSDN documentation, the term semantic logging refers specifically to the use of strongly typed events and consistent structure of log messages. In Service Fabric sematic logging is baked right into the platform and tooling. For example if we look at any auto generated .cs file for an actor, stateful or stateless service we see examples of logging via the ServiceEventSource or ActorEventSource classes:</p>
<p><code>ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(AcmeService).Name);</code></p>
<p>When an event such as the one above is logged, it includes a payload containing individual variables as typed values that match a pre-defined schema. Moreover as we’ll see later on in this article, when the event is routed to a suitable destination such as Application Insights, the event’s payload is written as discrete elements making it much easier to analyse, correlate and query. For those new to Application Insights the following <a href="https://azure.microsoft.com/en-us/documentation/articles/app-insights-overview/" target="_blank" rel="external">offical introduction</a> provides a good starting point. </p>
<p>Having briefly defined semantic logging and mentioning that it’s baked into Service Fabric we should clarify that ServiceEventSource and ActorEventSource inherit from EventSource, which in turn writes events to ETW. Event Tracing for Windows or more commonly ETW is an efficient kernel-level tracing facility built into Windows that logs kernel or application-defined events. </p>
<p>Given the above we now turn our attention to exporting these ETW events to Application Insights or for that matter to any other supported target via two libraries, the Microsoft library aptly named <a href="https://github.com/mspnp/semantic-logging/" target="_blank" rel="external">Semantic Logging</a> (formerly known as the Semantic Logging Application Block or SLAB) and the <a href="https://github.com/fidmor89/SLAB_AppInsights/" target="_blank" rel="external">SemanticLogging.ApplicationInsights</a> library (also known as SLAB_AppInsights).</p>
<p>As all my Service Fabric projects are in .Net Core xproj structure (see previous articles) I ended up contributing to Fidel’s excellent library by converting the SemanticLogging.ApplicationInsights project to .Net Core xproj. My humble contribution has been merged into the master SemanticLogging.ApplicationInsights branch by Fidel and is used in the rest of the article below. As the NuGet package is somewhat behind, we’ll first start by downloading the master branch directly from GitHub and by adding it to our Visual Studio 2015 solution. Your solution will end-up looking something like this:</p>
<p><img src="/images/006_AcmeServiceProjectSemanticLogging.jpg" alt="Semantic Logging & Application Insights"></p>
<p>In your Service Fabric service (in my example AcmeService) edit the project.json:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"title"</span>: <span class="string">"AcmeService"</span>,</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"AcmeService"</span>,</span><br><span class="line"> <span class="attr">"version"</span>: <span class="string">"1.0.0-*"</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">"buildOptions"</span>: {</span><br><span class="line"> <span class="attr">"emitEntryPoint"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"preserveCompilationContext"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"compile"</span>: {</span><br><span class="line"> <span class="attr">"exclude"</span>: [</span><br><span class="line"> <span class="string">"PackageRoot"</span></span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"dependencies"</span>: {</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric"</span>: <span class="string">"5.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric.Services"</span>: <span class="string">"2.1.150"</span>,</span><br><span class="line"> <span class="attr">"EnterpriseLibrary.SemanticLogging"</span>: <span class="string">"2.0.1406.1"</span>,</span><br><span class="line"> <span class="attr">"SemanticLogging.ApplicationInsights"</span>: <span class="string">"1.0.0-*"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.PlatformAbstractions"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.FileExtensions"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.Json"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.Binder"</span>: <span class="string">"1.0.0"</span></span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"frameworks"</span>: {</span><br><span class="line"> <span class="attr">"net46"</span>: {</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"runtimes"</span>: {</span><br><span class="line"> <span class="attr">"win7-x64"</span>: {}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Add an appsettings.Development.json file and make sure to set your ASPNETCORE_ENVIRONMENT variable accordingly. Moreover you will need to set the Application Insights InstrumentationKey.</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"Logging"</span>: {</span><br><span class="line"> <span class="attr">"IncludeScopes"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"LogLevel"</span>: {</span><br><span class="line"> <span class="attr">"Default"</span>: <span class="string">"Debug"</span>,</span><br><span class="line"> <span class="attr">"System"</span>: <span class="string">"Information"</span>,</span><br><span class="line"> <span class="attr">"Microsoft"</span>: <span class="string">"Information"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"ApplicationInsights"</span>: {</span><br><span class="line"> <span class="attr">"InstrumentationKey"</span>: <span class="string">"YOUR KEY GOES HERE"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>We’ll add an AppSettings class so that we can bind our settings file to a strongly typed object:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">AcmeService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AppSettings</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">AppSettings</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> ApplicationInsights = <span class="keyword">new</span> ApplicationInsightsOptions();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> ApplicationInsightsOptions ApplicationInsights { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ApplicationInsightsOptions</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> InstrumentationKey { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>In a previous article we looked out how to share Asp.Net Core appsettings.json with Service Fabric Microservices so we’ll re-use the same logic and create a ConfigurationHelper:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.Extensions.PlatformAbstractions;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Configuration;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AcmeService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">ConfigurationHelper</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> AppSettings <span class="title">GetAppSettings</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> appSettings = <span class="keyword">new</span> AppSettings();</span><br><span class="line"> <span class="keyword">var</span> configRoot = GetConfigurationRoot();</span><br><span class="line"> configRoot.Bind(appSettings);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> appSettings;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IConfigurationRoot <span class="title">GetConfigurationRoot</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> IConfigurationRoot configuration = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> basePath = PlatformServices.Default.Application.ApplicationBasePath;</span><br><span class="line"> <span class="keyword">var</span> environmentName = Environment.GetEnvironmentVariable(<span class="string">"ASPNETCORE_ENVIRONMENT"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">string</span>.IsNullOrEmpty(environmentName))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> configurationBuilder = <span class="keyword">new</span> ConfigurationBuilder()</span><br><span class="line"> .SetBasePath(basePath)</span><br><span class="line"> .AddJsonFile(<span class="string">$"appsettings.<span class="subst">{environmentName}</span>.json"</span>);</span><br><span class="line"></span><br><span class="line"> configuration = configurationBuilder.Build();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> configuration;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Now for the secret sauce, we create a LoggingHelper class which returns an ObservableEventListener. The class configures the Application Insights sink from the SemanticLogging.ApplicationInsights library:</p>
<p><code>listener.LogToApplicationInsights(...)</code></p>
<p>and subscribes to Service Fabric ServiceEventSource events using the Semantic Logging library:</p>
<p><code>listener.EnableEvents(ServiceEventSource.Current.Name, EventLevel.Verbose);</code></p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.ApplicationInsights.Extensibility;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Practices.EnterpriseLibrary.SemanticLogging;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Diagnostics.Tracing;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AcmeService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">LoggingHelper</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ObservableEventListener <span class="title">GetEventListener</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> ObservableEventListener listener = <span class="keyword">new</span> ObservableEventListener();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> appSettings = ConfigurationHelper.GetAppSettings();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (appSettings != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> TelemetryConfiguration.CreateDefault();</span><br><span class="line"> TelemetryConfiguration.Active.InstrumentationKey = appSettings.ApplicationInsights.InstrumentationKey;</span><br><span class="line"></span><br><span class="line"> listener.LogToApplicationInsights(TelemetryConfiguration.Active.InstrumentationKey, <span class="keyword">new</span> List<ITelemetryInitializer>(TelemetryConfiguration.Active.TelemetryInitializers).ToArray());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> listener.EnableEvents(ServiceEventSource.Current.Name, EventLevel.Verbose);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> ServiceEventSource.Current.Message(ex.ToString());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> listener;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>All that is now left is the addition of a “one liner” to your Service Fabric Microservice (Program.cs) to enable Semantic Logging:</p>
<p><code>private static readonly ObservableEventListener _listener = LoggingHelper.GetEventListener();</code></p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.Practices.EnterpriseLibrary.SemanticLogging;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Runtime;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Diagnostics;</span><br><span class="line"><span class="keyword">using</span> System.Threading;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">AcmeService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">internal</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> ObservableEventListener _listener = LoggingHelper.GetEventListener();</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> This is the entry point of the service host process.</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> ServiceRuntime.RegisterServiceAsync(<span class="string">"LoggingServiceType"</span>,</span><br><span class="line"> context => <span class="keyword">new</span> LoggingService(context)).GetAwaiter().GetResult();</span><br><span class="line"></span><br><span class="line"> ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, <span class="keyword">typeof</span>(LoggingService).Name);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Prevents this host process from terminating so services keep running.</span></span><br><span class="line"> Thread.Sleep(Timeout.Infinite);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception e)</span><br><span class="line"> {</span><br><span class="line"> ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());</span><br><span class="line"> <span class="keyword">throw</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>And that’s about it… how simple is it to get your Service Fabric application events sent to Application Insights! Given the event producer (your Service Fabric application) is decoupled from the target through the magic of ETW and Semantic Logging libraries, the exact same approach and with minimal code changes successfully allows me to target Elastic Search as the event target. In fact for your systems you might also prefer to send some events to Application Insights and others to an Elastic Search cluster. Lastly I would like to conclude by saying if you find any of the above useful in your projects do consider contributing to Fidel’s excellent library or by creating completely new sinks for Semantic Logging!</p>
<hr>
<h4 id="References"><a href="#References" class="headerlink" title="References"></a>References</h4><ol>
<li><a href="https://msdn.microsoft.com/en-us/library/dn440729(v=pandp.60).aspx" target="_blank" rel="external">https://msdn.microsoft.com/en-us/library/dn440729(v=pandp.60).aspx</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/dn775014(v=pandp.20).aspx" target="_blank" rel="external">https://msdn.microsoft.com/en-us/library/dn775014(v=pandp.20).aspx</a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363668.aspx" target="_blank" rel="external">https://msdn.microsoft.com/en-us/library/windows/desktop/aa363668.aspx</a></li>
<li><a href="https://github.com/mspnp/semantic-logging" target="_blank" rel="external">https://github.com/mspnp/semantic-logging</a></li>
<li><a href="https://github.com/fidmor89/SLAB_AppInsights" target="_blank" rel="external">https://github.com/fidmor89/SLAB_AppInsights</a></li>
<li><a href="https://azure.microsoft.com/en-us/documentation/articles/app-insights-overview/" target="_blank" rel="external">https://azure.microsoft.com/en-us/documentation/articles/app-insights-overview/</a></li>
</ol>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Azure/">Azure</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Share-Asp-Net-Core-appsettings-json-with-Service-Fabric-Microservices" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/07/10/Share-Asp-Net-Core-appsettings-json-with-Service-Fabric-Microservices/">Share Asp.Net Core appsettings.json with Service Fabric Microservices</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/07/10/Share-Asp-Net-Core-appsettings-json-with-Service-Fabric-Microservices/" class="article-date">
<time datetime="2016-07-10T12:19:00.000Z" itemprop="datePublished">2016-07-10</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>If you’ve been working with Service Fabric you would have most likely come across the need to store configuration variables somewhere. This usually means defining and overriding parameters in the following files across various projects:</p>
<p><code>ApplicationPackageRoot\ApplicationManifest.xml</code><br><code>ApplicationParameters\Local.xml</code><br><code>PackageRoot\Config\Settings.xml</code></p>
<p>As all my microservice and library projects have been converted over to the new .Net Core xproj structure, I wanted to consolidate and share the same settings .json files used in my .Net Core Web project across the entire solution whilst still maintaining the ability to deploy/publish individual microsevices. Taking inspiration from how this is achieved in .Net Core and as I’m targeting .Net Core RC2, I created the following appsettings.json files in my Web project, corresponding to the ASPNETCORE_ENVIRONMENT variable: </p>
<p><code>appsettings.json</code><br><code>appsettings.Development.json</code><br><code>appsettings.Staging.json</code><br><code>appsettings.Production.json</code></p>
<p>Example Web project appsettings.Development.json:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"Logging"</span>: {</span><br><span class="line"> <span class="attr">"IncludeScopes"</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">"LogLevel"</span>: {</span><br><span class="line"> <span class="attr">"Default"</span>: <span class="string">"Debug"</span>,</span><br><span class="line"> <span class="attr">"System"</span>: <span class="string">"Information"</span>,</span><br><span class="line"> <span class="attr">"Microsoft"</span>: <span class="string">"Information"</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="attr">"ApplicationInsights"</span>: {</span><br><span class="line"> <span class="attr">"InstrumentationKey"</span>: <span class="string">""</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>For completeness the Web project.json file should also define a custom publishOptions:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">"publishOptions": {</span><br><span class="line"> "include": [</span><br><span class="line"> "wwwroot",</span><br><span class="line"> "Views",</span><br><span class="line"> "appsettings.Development.json",</span><br><span class="line"> "appsettings.Staging.json",</span><br><span class="line"> "appsettings.Production.json",</span><br><span class="line"> "web.config"</span><br><span class="line"> ]</span><br><span class="line">},</span><br></pre></td></tr></table></figure>
<p>Next we need to create either a common .Net Core library project or within each microservice project add the following class:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.Extensions.PlatformAbstractions;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Configuration;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">Acme.Helpers</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">ConfigurationHelper</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ApplicationOptions <span class="title">GetConfiguration</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> appSettings = <span class="keyword">new</span> AppSettings();</span><br><span class="line"> <span class="keyword">var</span> configRoot = GetConfigurationRoot();</span><br><span class="line"> configRoot.Bind(appSettings);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IConfigurationRoot <span class="title">GetConfigurationRoot</span>(<span class="params"></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> IConfigurationRoot configuration = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> basePath = PlatformServices.Default.Application.ApplicationBasePath;</span><br><span class="line"> <span class="keyword">var</span> environmentName = Environment.GetEnvironmentVariable(Acme.ASPNETCORE_ENVIRONMENT);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">string</span>.IsNullOrEmpty(environmentName))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> configurationBuilder = <span class="keyword">new</span> ConfigurationBuilder()</span><br><span class="line"> .SetBasePath(basePath)</span><br><span class="line"> .AddJsonFile(<span class="string">$"appsettings.<span class="subst">{environmentName}</span>.json"</span>);</span><br><span class="line"></span><br><span class="line"> configuration = configurationBuilder.Build();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> configuration;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>Note: the value of Acme.ASPNETCORE_ENVIRONMENT is "ASPNETCORE_ENVIRONMENT". Make sure ASPNETCORE_ENVIRONMENT is set on your target environment accordingly. Moreover the AppSettings class definition must correspond to the content of your appsettings.json file, as otherwise the configRoot.Bind(appSettings) will fail.</code></p>
<p>In each microservice project.json we’ll also need to add custom postcompile and postpublish scripts, with the complete file looking something like this:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"title"</span>: <span class="string">"Acme.Service.Clock"</span>,</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"Acme.Service.Clock"</span>,</span><br><span class="line"> <span class="attr">"version"</span>: <span class="string">"1.0.0-*"</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">"buildOptions"</span>: {</span><br><span class="line"> <span class="attr">"emitEntryPoint"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"preserveCompilationContext"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"compile"</span>: {</span><br><span class="line"> <span class="attr">"exclude"</span>: [</span><br><span class="line"> <span class="string">"PackageRoot"</span></span><br><span class="line"> ]</span><br><span class="line"> } </span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"dependencies"</span>: {</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric"</span>: <span class="string">"5.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric.Actors"</span>: <span class="string">"2.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric.Data"</span>: <span class="string">"2.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric.Services"</span>: <span class="string">"2.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Framework.Configuration"</span>: <span class="string">"1.0.0-beta8"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Framework.Configuration.Json"</span>: <span class="string">"1.0.0-beta8"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.EnvironmentVariables"</span>: <span class="string">"1.0.0-rc2-final"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.PlatformAbstractions"</span>: <span class="string">"1.0.0-rc2-final"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.Binder"</span>: <span class="string">"1.0.0-rc2-final"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.FileExtensions"</span>: <span class="string">"1.0.0-rc2-final"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.Extensions.Configuration.Json"</span>: <span class="string">"1.0.0-rc2-final"</span></span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"scripts"</span>: {</span><br><span class="line"> <span class="attr">"postcompile"</span>: [</span><br><span class="line"> <span class="string">"xcopy /Y ..\\Web\\appsettings.Development.json %compile:OutputDir%\\win7-x64\\appsettings.Development.json*"</span>,</span><br><span class="line"> <span class="string">"xcopy /Y ..\\Web\\appsettings.Staging.json %compile:OutputDir%\\win7-x64\\appsettings.Staging.json*"</span>,</span><br><span class="line"> <span class="string">"xcopy /Y ..\\Web\\appsettings.Production.json %compile:OutputDir%\\win7-x64\\appsettings.Production.json*"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"postpublish"</span>: [</span><br><span class="line"> <span class="string">"xcopy /Y ..\\Web\\appsettings.Development.json %publish:OutputPath%"</span>,</span><br><span class="line"> <span class="string">"xcopy /Y ..\\Web\\appsettings.Staging.json %publish:OutputPath%"</span>,</span><br><span class="line"> <span class="string">"xcopy /Y ..\\Web\\appsettings.Production.json %publish:OutputPath%"</span></span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"frameworks"</span>: {</span><br><span class="line"> <span class="attr">"net46"</span>: { }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"runtimes"</span>: {</span><br><span class="line"> <span class="attr">"win7-x64"</span>: { }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>Note: for the scripts to work, adjust the location of the appsettings.json files to be relative to your solution and project structure</code></p>
<p>With the above changes in place, whenever you now compile your microservice projects or deploy/publish them to a Service Fabric cluster, the corresponding appsettings.json files will also be copied, packaged and deployed! Moreover access to configuration variables within the appsettings.json file is achieved through the same low friction and strongly typed/bound mechanism as used in Asp.Net Core projects. The code would look something like:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> configuration = ConfigurationHelper.GetConfiguration();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (configuration != <span class="literal">null</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> instrumentationKey = configuration.ApplicationInsights.InstrumentationKey;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>A final word, for production scenarios it is recommended that the content of appsettings.json be encrypted, in addition to the above ConfigurationHelper code being extended to support reloadOnChange events. Maybe a topic for future posts…</p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Create-or-convert-your-Service-Fabric-Microservices-to-Net-Core-xproj-structure" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/07/06/Create-or-convert-your-Service-Fabric-Microservices-to-Net-Core-xproj-structure/">Create or convert your Service Fabric Microservices to .Net Core xproj structure</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/07/06/Create-or-convert-your-Service-Fabric-Microservices-to-Net-Core-xproj-structure/" class="article-date">
<time datetime="2016-07-06T12:29:00.000Z" itemprop="datePublished">2016-07-06</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>In a previous article we walked through the process of hosting our Asp.Net Core Web Microservice within Service Fabric and also self-hosting outside Service Fabric via Kestrel for development and debugging. Today we’ll discuss how we can create a new .Net Core Service Fabric Microservice targeting the full stack (.net46), given the VS 2015 template only supports web projects currently. Note that similar principles would apply to converting existing Microservice projects to .Net Core xproj structure.</p>
<p>To begin, in Visual Studio 2015 add a new Service Fabric Project, in my example a Stateful Service named AcmeService:</p>
<p><img src="/images/002_AcmeService.jpg" alt="New Service Fabric project"></p>
<p>Once complete you should have a solution resembling the below:</p>
<p><img src="/images/003_AcmeSolution.jpg" alt="Service Fabric solution"></p>
<p>What we do next is remove the AcmeService project from the solution altogether and rename the folder to AcmeService.tmp. We will re-create the project as a .Net Core Console Application. Select Add New Project and select Console Application (.Net Core), making sure the location is the same as the original and enter AcmeService as the project name:</p>
<p><img src="/images/004_AcmeProjectAsXproj.jpg" alt="AcmeService as a .Net Core Console project"></p>
<p>From the AcmeService.tmp folder copy:</p>
<p><code>PackageRoot folder</code><br><code>Properties folder</code><br><code>AcmeService.cs (copy over target file)</code><br><code>Program.cs</code><br><code>ServiceEventSource.cs</code></p>
<p>to the AcmeService folder, your solution resembling:</p>
<p><img src="/images/005_FinalAcmeProjectAsXproj.jpg" alt="Service Fabric solution with .Net Core xproj structure"></p>
<p>Copy the contents of the below into your project.json file:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"title"</span>: <span class="string">"AcmeService"</span>,</span><br><span class="line"> <span class="attr">"description"</span>: <span class="string">"AcmeService"</span>,</span><br><span class="line"> <span class="attr">"copyright"</span>: <span class="string">"Copyright © Acme 2016"</span>,</span><br><span class="line"> <span class="attr">"version"</span>: <span class="string">"1.0.0-*"</span>,</span><br><span class="line"></span><br><span class="line"> <span class="attr">"buildOptions"</span>: {</span><br><span class="line"> <span class="attr">"emitEntryPoint"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"preserveCompilationContext"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"compile"</span>: {</span><br><span class="line"> <span class="attr">"exclude"</span>: [</span><br><span class="line"> <span class="string">"PackageRoot"</span></span><br><span class="line"> ]</span><br><span class="line"> } </span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"dependencies"</span>: {</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric"</span>: <span class="string">"5.1.150"</span>,</span><br><span class="line"> <span class="attr">"Microsoft.ServiceFabric.Services"</span>: <span class="string">"2.1.150"</span></span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"frameworks"</span>: {</span><br><span class="line"> <span class="attr">"net46"</span>: { }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="attr">"runtimes"</span>: {</span><br><span class="line"> <span class="attr">"win7-x64"</span>: { }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Lastly we have to add back our .Net Core Console Application project by right clicking on the Service Fabric project and selecting Add Existing Service Fabric Service. You might get a warning about updating but just click OK. You can also delete the AcmeService.tmp folder as it’s no longer needed.</p>
<p>To compile you can use Visual Studio or at a command prompt you can issue normal dotnet.exe commands, for example:</p>
<p><code>dotnet.exe build</code></p>
<p>In the next series of articles we’ll look at some more advanced topics such as sharing appsettings.json files between Web and other Microservice projects, as well as logging to Application Insights.</p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Asp-Net-Core-with-Kestrel-and-Service-Fabric" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/07/05/Asp-Net-Core-with-Kestrel-and-Service-Fabric/"> Asp.Net Core with Kestrel and Service Fabric</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/07/05/Asp-Net-Core-with-Kestrel-and-Service-Fabric/" class="article-date">
<time datetime="2016-07-05T11:32:00.000Z" itemprop="datePublished">2016-07-05</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Service Fabric SDK 2.1.150 comes with an ASP.NET Core project template so you can easily include a web app or web service in your Service Fabric application. To get started follow this official article: <a href="https://azure.microsoft.com/en-us/documentation/articles/service-fabric-add-a-web-frontend" target="_blank" rel="external">Build a web service front end for your application</a>, but for more advanced scenarios such as hosting your .Net Core web application outside Service Fabric (for those times you just don’t want to deploy), forcing Kestrel to listen to all machine assigned IP addresses, we’ll customise and extend the starter template generated code. Moreover, with .Net Core RC2 and RTM the ubiquitous dotnet.exe becomes our preferred tool of choice so let’s facilitate running your Service Fabric Web app for development and debugging with the same simple command dotnet.exe run.</p>
<p>As always and given I am still targeting .Net Core RC2, we’ll start with the required project.json dependencies which should look something like the below. For command line argument heavily lifting include the “CommandLineParser”: “2.0.275-beta” package.</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">"dependencies": {</span><br><span class="line"> "Microsoft.AspNetCore.Hosting": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000004",</span><br><span class="line"> "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final", </span><br><span class="line"> "Microsoft.Extensions.Configuration.Abstractions": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.9.302261508-alpha",</span><br><span class="line"> "Microsoft.Extensions.Configuration.Binder": "1.0.0-rc2-final",</span><br><span class="line"> "Swashbuckle": "6.0.0-beta9",</span><br><span class="line"> "Swashbuckle.SwaggerUi": "6.0.0-beta9",</span><br><span class="line"> "Swashbuckle.SwaggerGen": "6.0.0-beta9",</span><br><span class="line"> "CommandLineParser": "2.0.275-beta",</span><br><span class="line"> "Microsoft.ServiceFabric": "5.1.150",</span><br><span class="line"> "Microsoft.ServiceFabric.Data": "2.1.150",</span><br><span class="line"> "Microsoft.ServiceFabric.Services": "2.1.150",</span><br><span class="line"> "Microsoft.AspNetCore.Http.Abstractions": "1.0.0-rc2-final"</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>In your Program.cs which contains the generated starter template code add the following usings:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> CommandLine;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Communication.Runtime;</span><br><span class="line"><span class="keyword">using</span> Microsoft.ServiceFabric.Services.Runtime;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Fabric;</span><br><span class="line"><span class="keyword">using</span> System.Threading;</span><br><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br></pre></td></tr></table></figure>
<p>Since we want to host our .Net Core web application both within Service Fabric and outside for quick turnaround during development and debugging without the hassle of always deploying, we modify Main to support both scenarios:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span><br><span class="line"></span>{</span><br><span class="line"> <span class="keyword">var</span> parser = <span class="keyword">new</span> Parser(with => {</span><br><span class="line"> with.EnableDashDash = <span class="literal">true</span>;</span><br><span class="line"> with.HelpWriter = Console.Out;</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> result = parser.ParseArguments<Options>(args);</span><br><span class="line"></span><br><span class="line"> result.MapResult(</span><br><span class="line"> options => </span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (options.Host.ToLower() == AcmeConstants.ServiceFabricHost)</span><br><span class="line"> {</span><br><span class="line"> ServiceRuntime.RegisterServiceAsync(<span class="string">"WebType"</span>, context => <span class="keyword">new</span> WebHostingService(context, <span class="string">"WebTypeEndpoint"</span>)).GetAwaiter().GetResult();</span><br><span class="line"> Thread.Sleep(Timeout.Infinite);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">else</span> <span class="title">if</span>(<span class="params">options.Host.ToLower(</span>) </span>== AcmeConstants.SelfHost)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> host = WebHostBuilderHelper.GetWebHost(<span class="keyword">new</span> WebHostBuilder(), options.Protocol, options.Port))</span><br><span class="line"> {</span><br><span class="line"> host.Run();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }, </span><br><span class="line"> errors => </span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>AcmeConstants.ServiceFabricHost</code> - value of command line argument: service-fabric-host<br><code>AcmeConstants.SelfHost</code> - value of command line argument: self-host</p>
<p>We then need to create an Options class to be used by the CommandLineParser:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">internal</span> <span class="keyword">sealed</span> <span class="keyword">class</span> <span class="title">Options</span></span><br><span class="line">{</span><br><span class="line"> [Option(Default = <span class="string">"self-host"</span>, HelpText = <span class="string">"The target host - Options [self-host] or [service-fabric-host]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Host { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [Option(Default = <span class="string">"http"</span>, HelpText = <span class="string">"The target protocol - Options [http] or [https]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Protocol { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [Option(Default = <span class="string">"localhost"</span>, HelpText = <span class="string">"The target IP Address or Uri - Example [localhost] or [127.0.0.1]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> IpAddressOrFQDN { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [Option(Default = <span class="string">"5000"</span>, HelpText = <span class="string">"The target port - Example [80] or [5000]"</span>)]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Port { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>We replace the generated OpenAsync code with the following version:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Task<<span class="keyword">string</span>> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);</span><br><span class="line"> <span class="keyword">string</span> serverUrl = <span class="string">$"<span class="subst">{endpoint.Protocol}</span>://<span class="subst">{FabricRuntime.GetNodeContext().IPAddressOrFQDN}</span>:<span class="subst">{endpoint.Port}</span>"</span>;</span><br><span class="line"></span><br><span class="line"> _webHost = WebHostBuilderHelper.GetWebHost(<span class="keyword">new</span> WebHostBuilder(), endpoint.Protocol.ToString(), endpoint.Port.ToString());</span><br><span class="line"> _webHost.Start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Task.FromResult(serverUrl);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Lastly we create our common GetWebHost method: </p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">WebHostBuilderHelper</span></span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IWebHost <span class="title">GetWebHost</span>(<span class="params">IWebHostBuilder webHostBuilder, <span class="keyword">string</span> protocol, <span class="keyword">string</span> port</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> IWebHost webHost = webHostBuilder</span><br><span class="line"> .UseKestrel()</span><br><span class="line"> .UseContentRoot(Directory.GetCurrentDirectory())</span><br><span class="line"> .UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), <span class="string">"wwwroot"</span>))</span><br><span class="line"> .UseUrls(<span class="string">$"<span class="subst">{protocol}</span>://+:<span class="subst">{port}</span>"</span>)</span><br><span class="line"> .UseStartup<Startup>()</span><br><span class="line"> .Build();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> webHost;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Note, I prefer to use the following which instructs Kestrel to listen to all IP addresses assigned to the machine on the port specified:</p>
<p><code>.UseUrls($"{protocol}://+:{port}")</code></p>
<p>All that is now left to do is within your .Net Core Web Application PackageRoot, edit the ServiceManifest.xml CodePackage so that we tell Web.exe to “host” within Service Fabric in this scenario:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">CodePackage</span> <span class="attr">Name</span>=<span class="string">"C"</span> <span class="attr">Version</span>=<span class="string">"1.0.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">EntryPoint</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ExeHost</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Program</span>></span>Web.exe<span class="tag"></<span class="name">Program</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Arguments</span>></span>--host service-fabric-host<span class="tag"></<span class="name">Arguments</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">WorkingFolder</span>></span>CodePackage<span class="tag"></<span class="name">WorkingFolder</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ConsoleRedirection</span> <span class="attr">FileRetentionCount</span>=<span class="string">"5"</span> <span class="attr">FileMaxSizeInKb</span>=<span class="string">"2048"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">ExeHost</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">EntryPoint</span>></span></span><br><span class="line"><span class="tag"></<span class="name">CodePackage</span>></span></span><br></pre></td></tr></table></figure>
<p>Your .Net Core Web application will now run both within Service Fabric and in debug mode. To run from the command line, from within your Web application folder issue:</p>
<p><code>dotnet.exe run</code></p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Deploy-a-Service-Fabric-Cluster-with-support-for-net-4-6" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/06/23/Deploy-a-Service-Fabric-Cluster-with-support-for-net-4-6/">Deploy a Service Fabric Cluster to Azure with .NET Framework 4.6 (ARM template)</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/06/23/Deploy-a-Service-Fabric-Cluster-with-support-for-net-4-6/" class="article-date">
<time datetime="2016-06-23T13:13:00.000Z" itemprop="datePublished">2016-06-23</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>For anyone working with Service Fabric and wishing to build a solution targeting the .NET Framework 4.6, deploying to Azure is a challenge given this version of the framework is not yet available in the default Windows Server 2012 image. </p>
<p>To overcome the above limitation and to make the process as easy as possible, we’ll employ a customised Azure Resource Manager (ARM) template which we’ll first generate via the Azure Portal. To get started simply click on the following link <a href="https://portal.azure.com/#create/Microsoft.ServiceFabricCluster" target="_blank" rel="external">https://portal.azure.com/#create/Microsoft.ServiceFabricCluster</a> or in Azure Marketplace search for Service Fabric Cluster.</p>
<p>As there is great guidance and content on Microsoft’s Azure portal, I won’t repeat the steps on Deploying a Service Fabric Cluster using an ARM template however I will ask you to complete all the fields as you normally would (login, password, custom ports for http and https), but instead of pressing create and deploying your cluster we’ll opt to download the ARM template.</p>
<p>Opening the ARM template in Visual Code or your text editor of choice, search for the following json section:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">"virtualMachineProfile": {</span><br><span class="line"> "extensionProfile": {</span><br><span class="line"> "extensions": [</span><br></pre></td></tr></table></figure>
<p>Then add a custom script extension block with the following content:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "name":"CustomScriptExtensionInstallNet46",</span><br><span class="line"> "properties":{</span><br><span class="line"> "publisher":"Microsoft.Compute",</span><br><span class="line"> "type":"CustomScriptExtension",</span><br><span class="line"> "typeHandlerVersion":"1.7",</span><br><span class="line"> "autoUpgradeMinorVersion":false,</span><br><span class="line"> "settings":{</span><br><span class="line"> "fileUris":[</span><br><span class="line"> "https://serviceprofiler.azurewebsites.net/content/downloads/InstallNetFx46.ps1"</span><br><span class="line"> ],</span><br><span class="line"> "commandToExecute":"powershell.exe -ExecutionPolicy Unrestricted -File InstallNetFx46.ps1"</span><br><span class="line"> },</span><br><span class="line"> "forceUpdateTag":"RerunExtension"</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure>
<blockquote><p>Note, for additional security & flexibility self-host a copy of the InstallNetFx46.ps1 script file.</p>
</blockquote>
<p>Download the ARM template, in my case I also rename the file to correspond to the environment targeted (for example AcmeServiceFabricUATCluster.json) and open a PowerShell window to the same folder. Then issue the following commands, substituting the Acme values for your own:</p>
<p><code>Login-AzureRmAccount</code></p>
<p><code>Get-AzureRmSubscription –SubscriptionName "AcmeCorp" | Select-AzureRmSubscription</code></p>
<p><code>New-AzureRmResourceGroup -Name AcmeUAT -Location "West US"</code></p>
<p><code>New-AzureRmResourceGroupDeployment -Name AcmeUATDeployment -ResourceGroupName AcmeUAT -TemplateFile AcmeServiceFabricUATCluster.json</code></p>
<p>Enter the values requested such as Password if executing interactively. Upon ARM completion your Service Fabric nodes will now contain an installation of .NET Framework 4.6… How simple! The next step is to deploy your solution…</p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Azure/">Azure</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/DevOps/">DevOps</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Asp-Net-Core-MVC-6-OpenIdConnect-JWT-and-Angular-2-SPA-Part-2" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/06/14/Asp-Net-Core-MVC-6-OpenIdConnect-JWT-and-Angular-2-SPA-Part-2/">Asp.Net Core RC2, OpenIdConnect, JWT, Swagger, AutoRest and Angular 2 SPA - Part 2</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/06/14/Asp-Net-Core-MVC-6-OpenIdConnect-JWT-and-Angular-2-SPA-Part-2/" class="article-date">
<time datetime="2016-06-13T15:52:00.000Z" itemprop="datePublished">2016-06-14</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Continuing on from a previous post this article details my journey in upgrading a Service Fabric multi-tenant application from .Net Core RC1 to RC2, which turned out to be a breaking albeit worthwhile change, specifically for the Startup.cs class and related boot strapping code for Swagger, CookieAuthentication, OpenIdConnectAuthentication and JwtBearerAuthentication. In subsequent posts we’ll explore how .Net Core RC2 hosts web applications but for now let’s look at the first challenge encountered during the upgrade, which was to chase down all required libraries that are also .Net Core RC2 compatible.</p>
<blockquote><p>As of the time of writing, I could only get Swashbuckle version 6.0.0-beta9 to work with .Net Core RC2. </p>
<p>The below code supports multi-tenant Azure AD authentication and is meant for development scenarios as ValidateIssuer and RequireHttpsMetadata are both set to false for simplicity.</p>
</blockquote>
<p>The full dependencies section of your project.json should look something like this:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">"dependencies": {</span><br><span class="line"> "Microsoft.AspNetCore.Hosting": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000004",</span><br><span class="line"> "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Mvc.Core": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",</span><br><span class="line"> "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.9.302261508-alpha",</span><br><span class="line"> "Microsoft.Extensions.Configuration.Binder": "1.0.0-rc2-final",</span><br><span class="line"> "Swashbuckle": "6.0.0-beta9",</span><br><span class="line"> "Swashbuckle.SwaggerUi": "6.0.0-beta9",</span><br><span class="line"> "Swashbuckle.SwaggerGen": "6.0.0-beta9"</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Your Startup.cs usings should look something like the below:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Authentication.Cookies;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Authentication.OpenIdConnect;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Builder;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Http;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Configuration;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</span><br><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Logging;</span><br><span class="line"><span class="keyword">using</span> Microsoft.IdentityModel.Tokens;</span><br><span class="line"><span class="keyword">using</span> Newtonsoft.Json.Serialization;</span><br><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Net;</span><br></pre></td></tr></table></figure>
<p>Having sourced the relevant libraries and compatible versions, it’s now time to turn our attention to the ConfigureServices method wherein we’ll setup Swagger, tweak Json formatting for JavaScript clients such as our Angular 2 SPA, and finally also tweak how AutoRest generates client code. I want AutoRest to generate separate files per server side controller which is achieved through a custom SwaggerOperationNameFilter.</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> IServiceProvider <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span><br><span class="line"></span>{</span><br><span class="line"> <span class="comment">// Add MVC service</span></span><br><span class="line"> services.AddMvc().AddJsonOptions(options =></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// Support for JavaScript clients which assume CamelCase - starting with lower case</span></span><br><span class="line"> options.SerializerSettings.ContractResolver = <span class="keyword">new</span> CamelCasePropertyNamesContractResolver();</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Add Swagger API service</span></span><br><span class="line"> services.AddSwaggerGen();</span><br><span class="line"> services.ConfigureSwaggerGen(options =></span><br><span class="line"> {</span><br><span class="line"> options.SingleApiVersion(<span class="keyword">new</span> Swashbuckle.SwaggerGen.Generator.Info</span><br><span class="line"> {</span><br><span class="line"> Version = <span class="string">"v1"</span>,</span><br><span class="line"> Title = <span class="string">"Acme API"</span>,</span><br><span class="line"> Description = <span class="string">"Acme API Home"</span>,</span><br><span class="line"> TermsOfService = <span class="string">"Legal"</span></span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Controls how tools like AutoRest generate client code (separate files per server side controller)</span></span><br><span class="line"> options.OperationFilter<SwaggerOperationNameFilter>();</span><br><span class="line"> options.DescribeStringEnumsInCamelCase();</span><br><span class="line"> options.DescribeAllEnumsAsStrings();</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> acmeOptions = <span class="keyword">new</span> AcmeOptions();</span><br><span class="line"> Configuration.Bind(acmeOptions);</span><br><span class="line"> services.AddSingleton(acmeOptions);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Configure IoC service</span></span><br><span class="line"> <span class="keyword">var</span> builder = <span class="keyword">new</span> ContainerBuilder();</span><br><span class="line"> builder.Populate(services);</span><br><span class="line"> <span class="keyword">var</span> container = builder.Build();</span><br><span class="line"> <span class="keyword">return</span> container.Resolve<IServiceProvider>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Code for the custom SwaggerOperationNameFilter:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">internal</span> <span class="keyword">class</span> <span class="title">SwaggerOperationNameFilter</span> : <span class="title">IOperationFilter</span></span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Apply</span>(<span class="params">Operation operation, OperationFilterContext context</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> operation.OperationId = context.ApiDescription.GroupName + <span class="string">"_"</span> + operation.OperationId;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Concluding the changes required for the .Net Core RC2 upgrade, we dive into the Configure method. Canny readers will notice that UseCookieAuthentication, UseOpenIdConnectAuthentication and UseJwtBearerAuthentication have been refactored to handle options in a more consistent manner with the rest of the .Net Core APIs. </p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory</span>)</span><br><span class="line"></span>{ </span><br><span class="line"> <span class="keyword">if</span> (env.IsDevelopment())</span><br><span class="line"> {</span><br><span class="line"> app.UseDeveloperExceptionPage();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> app.UseStaticFiles();</span><br><span class="line"></span><br><span class="line"> app.UseCookieAuthentication(<span class="keyword">new</span> CookieAuthenticationOptions</span><br><span class="line"> {</span><br><span class="line"> AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,</span><br><span class="line"> AutomaticAuthenticate = <span class="literal">true</span>,</span><br><span class="line"> AutomaticChallenge = <span class="literal">true</span>,</span><br><span class="line"> CookieSecure = CookieSecureOption.Never,</span><br><span class="line"> <span class="comment">// The default setting for cookie expiration is 14 days. SlidingExpiration is set to true by default</span></span><br><span class="line"> ExpireTimeSpan = TimeSpan.FromHours(<span class="number">1</span>),</span><br><span class="line"> SlidingExpiration = <span class="literal">true</span></span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> acmeOptions = app.ApplicationServices.GetService<AcmeOptions>();</span><br><span class="line"></span><br><span class="line"> app.UseOpenIdConnectAuthentication(<span class="keyword">new</span> OpenIdConnectOptions</span><br><span class="line"> {</span><br><span class="line"> AutomaticAuthenticate = <span class="literal">true</span>,</span><br><span class="line"> AutomaticChallenge = <span class="literal">true</span>,</span><br><span class="line"> ClientId = acmeOptions.ClientId,</span><br><span class="line"> Authority = AcmeConstants.AuthEndpointPrefix + <span class="string">"common/"</span>,</span><br><span class="line"> PostLogoutRedirectUri = acmeOptions.PostLogoutRedirectUri,</span><br><span class="line"> CallbackPath = AcmeRouteConstants.LoginCallbackRoute,</span><br><span class="line"> SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,</span><br><span class="line"> AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,</span><br><span class="line"> TokenValidationParameters = <span class="keyword">new</span> TokenValidationParameters { ValidateIssuer = <span class="literal">false</span> },</span><br><span class="line"> RequireHttpsMetadata = <span class="literal">false</span>,</span><br><span class="line"> Events = <span class="keyword">new</span> OpenIdConnectAuthenticationEvents(acmeOptions)</span><br><span class="line"> {</span><br><span class="line"> OnAuthenticationFailed = context => OpenIdConnectAuthenticationEvents.GetFailedResponse(context)</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Add JwtBearerAuthentication middleware </span></span><br><span class="line"> app.UseJwtBearerAuthentication(<span class="keyword">new</span> JwtBearerOptions</span><br><span class="line"> {</span><br><span class="line"> AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme,</span><br><span class="line"> Audience = acmeOptions.JwtAudience,</span><br><span class="line"> AutomaticAuthenticate = <span class="literal">true</span>,</span><br><span class="line"> AutomaticChallenge = <span class="literal">true</span>,</span><br><span class="line"> Authority = AcmeConstants.AuthEndpointPrefix + <span class="string">"common/"</span>,</span><br><span class="line"> TokenValidationParameters = <span class="keyword">new</span> TokenValidationParameters</span><br><span class="line"> {</span><br><span class="line"> ValidateIssuer = <span class="literal">false</span>,</span><br><span class="line"> },</span><br><span class="line"> RequireHttpsMetadata = <span class="literal">false</span>,</span><br><span class="line"> Events = <span class="keyword">new</span> JwtBearerAuthenticationEvents(acmeOptions)</span><br><span class="line"> {</span><br><span class="line"> OnAuthenticationFailed = context => JwtBearerAuthenticationEvents.GetFailedResponse(context)</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> app.UseMvc(routes =></span><br><span class="line"> {</span><br><span class="line"> routes.MapRoute(</span><br><span class="line"> name: <span class="string">"webapi"</span>,</span><br><span class="line"> template: <span class="string">"api/{controller}/{action}/{id?}"</span>);</span><br><span class="line"></span><br><span class="line"> routes.MapSpaFallbackRoute(<span class="string">"spa-fallback"</span>, <span class="keyword">new</span> { controller = <span class="string">"Home"</span>, action = <span class="string">"Index"</span> });</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Enable Use of Swagger</span></span><br><span class="line"> app.UseSwaggerGen();</span><br><span class="line"> app.UseSwaggerUi();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>If you’re wondering why I left the Microsoft.IdentityModel.Clients.ActiveDirectory library at “3.9.302261508-alpha”, in upcoming posts we’ll detail a strategy for automated integration testing of your .Net Core APIs using xUnit and optionally a BDD approach (SpecFlow), but more on that topic soon…</p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Security/">Security</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-Visual-Studio-Online-Powershell-steps-snippets" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/06/04/Visual-Studio-Online-Powershell-steps-snippets/">Visual Studio Online build step task snippets</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/06/04/Visual-Studio-Online-Powershell-steps-snippets/" class="article-date">
<time datetime="2016-06-03T14:00:00.000Z" itemprop="datePublished">2016-06-04</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Visual Studio Online build definitions & tasks have certainly come a long way since the old xaml template days. There is even a <a href="https://marketplace.visualstudio.com/vsts/" target="_blank" rel="external">VSO extensions</a> market place. For my current project we are using good old PowerShell based tasks which work together to continuously deploy the solution from VSO to a single node Azure VM Service Fabric cluster which runs all unit, integration and automated UI tests. Some PowerShell snippets which I’ve found helpful along the way are detailed below.</p>
<p>The following task snippet is used to check that a Service Fabric hosted Web endpoint is reachable and ready for integration and UI testing:</p>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$statuscode</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span>(<span class="variable">$statuscode</span> <span class="nomarkup">-ne</span> <span class="number">200</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">try</span> </span><br><span class="line"> {</span><br><span class="line"> <span class="variable">$statuscode</span> = (<span class="built_in">Invoke-WebRequest</span> -Uri <span class="string">"http://localhost"</span>).statuscode</span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">catch</span> </span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">Write-Host</span> <span class="string">"$(Get-Date) ....http://localhost is unreachable, sleeping for 30sec"</span></span><br><span class="line"> <span class="built_in">Start-Sleep</span> -s <span class="number">30</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="built_in">Write-Host</span> <span class="string">"$(Get-Date) ....http://localhost is now reachable, continuing"</span></span><br></pre></td></tr></table></figure>
<p>Logs the content of a test settings xml file and filters out any keys containing the string “Secret”:</p>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[xml]<span class="variable">$testSettings</span> = <span class="built_in">Get-Content</span> -Path <span class="variable">$args</span>[<span class="number">0</span>]</span><br><span class="line"><span class="variable">$testSettings</span>.configuration.appSettings.add | <span class="built_in">Where-Object</span> {<span class="variable">$_</span>.key <span class="nomarkup">-notmatch</span> <span class="string">"Secret"</span>}</span><br></pre></td></tr></table></figure>
<p>For xUnit tests the provided Visual Studio Test step can be configured with multiple target assemblies separated by a “;” character and wherever possible enable Run In Parallel. Also note to be careful not to end the line with a “;” character as VSO will think there is a missing assembly! For example:</p>
<p>Execution Options >> Test Assembly</p>
<p><code>$(Build.SourcesDirectory)\test\AcmeCore.Test\bin\$(BuildConfiguration)\AcmeCore.Test.dll;</code><br><code>$(Build.SourcesDirectory)\test\AcmeCommon.Test\bin\$(BuildConfiguration)\AcmeCommon.Test.dll</code></p>
<p>Advanced Execution Options >> Path to Custom Test Adapters</p>
<p><code>$(Build.SourcesDirectory)\packages\xunit.runner.visualstudio.2.2.0-beta1-build1144\build\_common\xunit.runner.visualstudio.testadapter.dll</code></p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/PowerShell/">PowerShell</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Visual-Studio-Online/">Visual Studio Online</a></li></ul>
</footer>
</div>
</article>
<article id="post-Asp-Net-Core-bug-within-AuthenticationFailed-middleware-on-redirect" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/29/Asp-Net-Core-bug-within-AuthenticationFailed-middleware-on-redirect/">Asp.Net Core RC1, OpenIdConnect, JWT and Angular 2 SPA - Part 1</a>
</h1>
</header>
<div class="article-meta">
<a href="/2016/03/29/Asp-Net-Core-bug-within-AuthenticationFailed-middleware-on-redirect/" class="article-date">
<time datetime="2016-03-28T13:00:00.000Z" itemprop="datePublished">2016-03-29</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Working with Asp.Net Core and Angular 2 at the time of writing may feel like a trail blazing experience, especially given the lack of documentation and stability in the underling frameworks, libraries and tools, leading to lost time in debugging and searching for answers. </p>
<p>In the hope of documenting some of my own recent experiences integrating these technologies and Microsoft’s micro-services framework <a href="https://azure.microsoft.com/en-us/services/service-fabric/" target="_blank" rel="external">Service Fabric</a>, I’ll dive into specific code areas which have proven fiddley. To start off I should preface that the version of Asp.Net Core I’m currently targeting is RC1 and some bugs and workarounds will not apply to subsequent framework versions. Moreover the Service Fabric version targeted is Service Fabric SDK (version 2.0.217) and Service Fabric Runtime (version 5.0.217).</p>
<p>To begin, we’ll start by configuring CookieAuthentication, OpenIdConnectAuthentication, JwtBearerAuthentication, Mvc & SPA routes in our Asp.Net Core Web project Startup.cs file. </p>
<blockquote><p>Note that the below code supports multi-tenant Azure AD authentication and is meant for development scenarios as ValidateIssuer and RequireHttpsMetadata are both set to false for simplicity.</p>
</blockquote>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory</span>)</span><br><span class="line"></span>{</span><br><span class="line"> <span class="keyword">if</span> (env.IsDevelopment())</span><br><span class="line"> {</span><br><span class="line"> app.UseBrowserLink();</span><br><span class="line"> app.UseDeveloperExceptionPage();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> app.UseIISPlatformHandler();</span><br><span class="line"> app.UseStaticFiles();</span><br><span class="line"></span><br><span class="line"> app.UseCookieAuthentication(options =></span><br><span class="line"> {</span><br><span class="line"> options.AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme;</span><br><span class="line"> options.AutomaticAuthenticate = <span class="literal">true</span>;</span><br><span class="line"> options.AutomaticChallenge = <span class="literal">true</span>;</span><br><span class="line"> options.CookieSecure = CookieSecureOption.Never;</span><br><span class="line"> <span class="comment">// The default setting for cookie expiration is 14 days. SlidingExpiration is set to true by default</span></span><br><span class="line"> options.ExpireTimeSpan = TimeSpan.FromHours(<span class="number">1</span>);</span><br><span class="line"> options.SlidingExpiration = <span class="literal">true</span>;</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> acmeOptions = app.ApplicationServices.GetService<IOptions<AcmeOptions>>().Value;</span><br><span class="line"></span><br><span class="line"> app.UseOpenIdConnectAuthentication(options =></span><br><span class="line"> {</span><br><span class="line"> options.AutomaticAuthenticate = <span class="literal">true</span>;</span><br><span class="line"> options.AutomaticChallenge = <span class="literal">true</span>;</span><br><span class="line"> options.ClientId = acmeOptions.AzureAd.ClientId;</span><br><span class="line"> options.Authority = AcmeConstants.AuthEndpointPrefix + <span class="string">"common/"</span>;</span><br><span class="line"> options.PostLogoutRedirectUri = acmeOptions.AzureAd.PostLogoutRedirectUri;</span><br><span class="line"> options.CallbackPath = AcmeRouteConstants.LoginCallbackRoute;</span><br><span class="line"> options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;</span><br><span class="line"> options.AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme;</span><br><span class="line"> options.TokenValidationParameters = <span class="keyword">new</span> TokenValidationParameters { ValidateIssuer = <span class="literal">false</span> };</span><br><span class="line"> options.RequireHttpsMetadata = <span class="literal">false</span>;</span><br><span class="line"> options.Events = <span class="keyword">new</span> OpenIdConnectAuthenticationEvents(acmeOptions.AzureAd)</span><br><span class="line"> {</span><br><span class="line"> OnAuthenticationFailed = context => OpenIdConnectAuthenticationEvents.GetFailedResponse(context)</span><br><span class="line"> };</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> app.UseJwtBearerAuthentication(options =></span><br><span class="line"> {</span><br><span class="line"> options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;</span><br><span class="line"> options.Audience = acmeOptions.AzureAd.JwtAudience;</span><br><span class="line"> options.AutomaticAuthenticate = <span class="literal">true</span>;</span><br><span class="line"> options.AutomaticChallenge = <span class="literal">true</span>;</span><br><span class="line"> options.Authority = AcmeConstants.Security.AuthEndpointPrefix + <span class="string">"common/"</span>;</span><br><span class="line"> options.TokenValidationParameters = <span class="keyword">new</span> TokenValidationParameters</span><br><span class="line"> { </span><br><span class="line"> ValidateIssuer = <span class="literal">false</span>,</span><br><span class="line"> };</span><br><span class="line"> options.RequireHttpsMetadata = <span class="literal">false</span>;</span><br><span class="line"> options.Events = <span class="keyword">new</span> JwtBearerAuthenticationEvents</span><br><span class="line"> {</span><br><span class="line"> OnAuthenticationFailed = context => JwtBearerAuthenticationEvents.GetFailedResponse(context)</span><br><span class="line"> };</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> app.UseMvc(routes =></span><br><span class="line"> {</span><br><span class="line"> routes.MapRoute(</span><br><span class="line"> name: <span class="string">"webapi"</span>,</span><br><span class="line"> template: <span class="string">"api/{controller}/{action}/{id?}"</span>);</span><br><span class="line"></span><br><span class="line"> routes.MapSpaFallbackRoute(<span class="string">"spa-fallback"</span>, <span class="keyword">new</span> { controller = <span class="string">"Home"</span>, action = <span class="string">"Index"</span> });</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Apart from both Jwt and OpenIdConnect support, of interest is the implementation of custom event overrides via the OpenIdConnectAuthenticationEvents and JwtBearerAuthenticationEvents classes. As OnAuthenticationFailed cannot be overridden within the derived event classes we wire up our custom logic as below:</p>
<p><code>OnAuthenticationFailed = context => OpenIdConnectAuthenticationEvents.GetFailedResponse(context)</code></p>
<p><code>OnAuthenticationFailed = context => JwtBearerAuthenticationEvents.GetFailedResponse(context)</code></p>
<p>In Part 2 we’ll upgrade our code to Asp.Net Core RC2 and add support for Swagger and AutoRest.</p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net-Core/">Asp.Net Core</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Security/">Security</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Service-Fabric/">Service Fabric</a></li></ul>
</footer>
</div>
</article>
<article id="post-SQL-Server-2014-Availability-Groups-Failover-Identity-column-behaviour" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2014/12/15/SQL-Server-2014-Availability-Groups-Failover-Identity-column-behaviour/"> SQL Server 2014 Availability Groups, Failover & Identity column behaviour</a>
</h1>
</header>
<div class="article-meta">
<a href="/2014/12/15/SQL-Server-2014-Availability-Groups-Failover-Identity-column-behaviour/" class="article-date">
<time datetime="2014-12-14T13:00:00.000Z" itemprop="datePublished">2014-12-15</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Many of us have been using SQL Server’s built-in Identity value feature for as long as we can remember but when running SQL Server in HA scenarios such as AlwaysOn Availability Groups, there are a couple of things to take into consideration.</p>
<p>According to MSDN documentation “Consecutive values after server restart or other failures – SQL Server might cache identity values for performance reasons and some of the assigned values can be lost during a database failure or server restart. This can result in gaps in the identity value upon insert. If gaps are not acceptable then the application should use its own mechanism to generate key values. Using a sequence generator with the NOCACHE option can limit the gaps to transactions that are never committed.”</p>
<p>Moreover we read elsewhere “when a table with less than 1000 rows that has an identity value is part of a database that is failed over in an AlwaysOn availability group, the identity is reseeded to 1000. If the identity value is already over 1000, no reseed occurs. This also occurs if you restart the server.”</p>
<p>For existing Applications using Sequence might not be an option. In this case a scarcely documented workaround might be to set a Start-up Parameter on the SQL Server Service: <strong>-t272.</strong> </p>
<p>The lowercase “t” is an internal trace flag that is used by SQL Server support engineers. </p>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/SQL-Server/">SQL Server</a></li></ul>
</footer>
</div>
</article>
<article id="post-Continuous-integration-deployment-of-Azure-Web-Roles-using-TFS-on-premise-and-WebDeploy-Part-1" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2014/12/15/Continuous-integration-deployment-of-Azure-Web-Roles-using-TFS-on-premise-and-WebDeploy-Part-1/"> Continuous integration & deployment of Azure Web Roles using TFS on-premise and WebDeploy - Part 1</a>
</h1>
</header>
<div class="article-meta">
<a href="/2014/12/15/Continuous-integration-deployment-of-Azure-Web-Roles-using-TFS-on-premise-and-WebDeploy-Part-1/" class="article-date">
<time datetime="2014-12-14T13:00:00.000Z" itemprop="datePublished">2014-12-15</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Much work has gone into making continuous integration & deployment of Azure Websites as simple and low friction as possible with support for Visual Studio Online, Git and BitBucket etc. For customers and teams still using TFS on-premise and working with Web Roles (Azure Cloud Services) the experience is a little more involved with many resorting to rolling their own solutions, TFS Build Templates and PowerShell scripts, if tools like Octopus Deploy and platforms like appveyor are beyond reach.</p>
<blockquote><p>Before we proceed a small caveat, in part one we’ll look at automating just the deployment of files and code which have changed. This should suffice for most single instance Dev and Test scenarios, where code is compiled and deployed on hourly, daily cycles. For Production you will also want to deploy the .cspkg file in case Azure reprovisions your Web Role instance(s).</p>
</blockquote>
<p>I’ll assume you already have knowledge of the TFS build process, in addition to having source and build TFS server environments configured, I’ll skip straight to the MSBuild parameters as this is where most of the magic happens. I’ll also assume you have the latest Azure SDK and Visual Studio files installed on your build server. By VS files I mean the files required by the BuildProcessTemplate, in my case a customised version of the DefaultTemplate.11.xaml. </p>
<p>PowerShell scripts could be used for the Cloud Service provisioning step however for simplicity I’ve opted for a manual step where I use the Microsoft Azure Publish Settings wizard in VS 2013 and do an initial once off deployment of my Web Role to Azure, making sure to enable Remote Desktop for all roles and Enable Web Deploy for all Web Roles. You will need to take note of the username and password created during this process as they are used later as MSBuild arguments.</p>
<p>Once you have successfully Published your Web Role to an Azure Staging Slot, you will need to repeat the process for the Production Slot, so that we can use the Swap Deployment Slots feature (endpoints have to match) once our tests have passed.</p>
<p>To create a new Dev build definition in Team Explorer I select my preferred Source Setting, Build Defaults, Trigger values, Build Template etc. Note in the MSBuild arguments below UserName and Password are values you defined when using the Microsoft Azure Publish Settings wizard in VS 2013. Moreover I am targeting a Debug build, specifying the VisualStudioVersion which for VS 2013 happens to be 12.0. The MsDeployServiceUrl will always be in the form <a href="https://xyz.cloudapp.net:8172/MSDeploy.axd" target="_blank" rel="external">https://xyz.cloudapp.net:8172/MSDeploy.axd</a> where xyz is either your Cloud Service name or the ID Azure generates for your Staging Web Role.</p>
<p>If you are unsure what this value needs to be go to Server Explorer and drill down the Azure Cloud Services until you find your Staging environment. Selecting the Staging node right click Properties: you will see the value for Name is in the form of a GUID. Use this value and replace xyz. To find your DeployIisAppPath value expand the Staging Node, expand the Web Role and select Instance 0, right click Properties: you will see a Name value in the form Contoso.Web_IN_0. Use this value as your DeployIisAppPath.</p>
<p>So putting it all together, your MSBuild arguments should look something like the following:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">/P:VisualStudioVersion=12.0 </span><br><span class="line">/P:Configuration=Debug </span><br><span class="line">/P:DeployOnBuild=True </span><br><span class="line">/P:DeployTarget=MSDeployPublish </span><br><span class="line">/P:MsDeployServiceUrl=<span class="string">"https://8f5c8aa194524f18bd2697675025fdab.cloudapp.net:8172/MSDeploy.axd"</span></span><br><span class="line">/P:DeployIisAppPath=<span class="string">"Contoso.Web_IN_0_Web"</span> </span><br><span class="line">/P:AllowUntrustedCertificate=True </span><br><span class="line">/P:MSDeployPublishMethod=WMsvc </span><br><span class="line">/P:CreatePackageOnPublish=True </span><br><span class="line">/P:UserName=<span class="string">"contosomasteruser"</span> </span><br><span class="line">/P:Password=<span class="string">"YourPasswordGoesHere"</span></span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Azure/">Azure</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/DevOps/">DevOps</a></li></ul>
</footer>
</div>
</article>
<article id="post-Uploading-an-Image-in-MVC-5-to-Azure-Blob-Storage" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2014/11/15/Uploading-an-Image-in-MVC-5-to-Azure-Blob-Storage/"> Uploading an Image in MVC 5 to Azure Blob Storage</a>
</h1>
</header>
<div class="article-meta">
<a href="/2014/11/15/Uploading-an-Image-in-MVC-5-to-Azure-Blob-Storage/" class="article-date">
<time datetime="2014-11-14T13:00:00.000Z" itemprop="datePublished">2014-11-15</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>An interesting customer requirement last week came up where I needed to upload an image in MVC 5 directly to Azure Blob Storage. A simplified version follows, removing some application specific logic and validation steps. To start off I first created a MVC model for image upload purposes.</p>
<p>ImageUploadBindingModel</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ImageUploadBindingModel</span></span><br><span class="line">{</span><br><span class="line"> [MaxFileSize(<span class="number">1000000</span>, ErrorMessage = <span class="string">"Maximum allowed file size is 1MB"</span>)]</span><br><span class="line"> [DataType(DataType.Upload)]</span><br><span class="line"> <span class="keyword">public</span> HttpPostedFileBase DisplayImageUpload { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>For additional validation control over the upload, create a custom ValidationAttribute called MaxFileSizeAttribute.</p>
<p>MaxFileSizeAttribute</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MaxFileSizeAttribute</span> : <span class="title">ValidationAttribute</span>, <span class="title">IClientValidatable</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> <span class="keyword">int</span> _maxFileSize;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MaxFileSizeAttribute</span>(<span class="params"><span class="keyword">int</span> maxFileSize</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> _maxFileSize = maxFileSize;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">bool</span> <span class="title">IsValid</span>(<span class="params"><span class="keyword">object</span> <span class="keyword">value</span></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> file = <span class="keyword">value</span> <span class="keyword">as</span> HttpPostedFileBase;</span><br><span class="line"> <span class="keyword">if</span> (file == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> file.ContentLength <= _maxFileSize;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">string</span> <span class="title">FormatErrorMessage</span>(<span class="params"><span class="keyword">string</span> name</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">base</span>.FormatErrorMessage(_maxFileSize.ToString());</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> IEnumerable <span class="title">GetClientValidationRules</span>(<span class="params">ModelMetadata metadata, ControllerContext context</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">var</span> rule = <span class="keyword">new</span> ModelClientValidationRule</span><br><span class="line"> {</span><br><span class="line"> ErrorMessage = FormatErrorMessage(_maxFileSize.ToString()),</span><br><span class="line"> ValidationType = <span class="string">"filesize"</span></span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> rule.ValidationParameters[<span class="string">"maxsize"</span>] = _maxFileSize;</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">return</span> rule;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Now let’s turn our attention to the actual Azure logic which streams the upload direct from MVC memory to Azure Blob Storage. Note that CloudConfigurationManager is used to source relevant connection information required by the Azure libraries, something I hope to cover in another post as it’s a very relevant topic for Cloud first .Net solutions.</p>
<p>AzureStorage.cs</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.WindowsAzure;</span><br><span class="line"><span class="keyword">using</span> Microsoft.WindowsAzure.Storage;</span><br><span class="line"><span class="keyword">using</span> Microsoft.WindowsAzure.Storage.Auth;</span><br><span class="line"><span class="keyword">using</span> Microsoft.WindowsAzure.Storage.Blob;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">namespace</span> <span class="title">Contoso.Helpers</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AzureStorage</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">UploadFromStream</span>(<span class="params"><span class="keyword">string</span> uniqueBlobName, <span class="keyword">string</span> blobContentType, Stream fileStream</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="comment">// Retrieve storage account from connection string</span></span><br><span class="line"> CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting(<span class="string">"StorageConnection"</span>));</span><br><span class="line"> <span class="comment">// Create the blob client</span></span><br><span class="line"> CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();</span><br><span class="line"> <span class="comment">// Retrieve reference to a previously created container</span></span><br><span class="line"> CloudBlobContainer container = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting(<span class="string">"StorageContainer"</span>));</span><br><span class="line"> <span class="comment">// Retrieve reference to a blob</span></span><br><span class="line"> CloudBlockBlob blockBlob = container.GetBlockBlobReference(uniqueBlobName);</span><br><span class="line"> <span class="comment">// Set Blob ContentType</span></span><br><span class="line"> blockBlob.Properties.ContentType = blobContentType;</span><br><span class="line"> <span class="comment">// Stream fileStream to Blob - Note: Overwrites any existing file</span></span><br><span class="line"> blockBlob.UploadFromStream(fileStream);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Lastly the Controller handling the upload.</p>
<p>UploadImage</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">UploadImage</span>(<span class="params">ImageUploadBindingModel model</span>)</span><br><span class="line"></span>{</span><br><span class="line"> ContosoIdentityUser user = <span class="keyword">await</span> UserManager.FindByIdAsync(User.Identity.GetUserId());</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (user == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!ModelState.IsValid)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> BadRequest(ModelState);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> validImageTypes = <span class="keyword">new</span> <span class="keyword">string</span>[]</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"image/gif"</span>,</span><br><span class="line"> <span class="string">"image/jpeg"</span>,</span><br><span class="line"> <span class="string">"image/pjpeg"</span>,</span><br><span class="line"> <span class="string">"image/png"</span></span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (model.DisplayImageUpload != <span class="literal">null</span> && model.DisplayImageUpload.ContentLength > <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (!validImageTypes.Contains(model.DisplayImageUpload.ContentType))</span><br><span class="line"> {</span><br><span class="line"> ModelState.AddModelError(<span class="string">"DisplayImageUpload"</span>, <span class="string">"Supported Display Image formats: GIF, JPG or PNG."</span>);</span><br><span class="line"> <span class="keyword">return</span> BadRequest(ModelState);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (model.DisplayImageUpload != <span class="literal">null</span> && model.DisplayImageUpload.ContentLength > <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">string</span> uniqueBlobName = <span class="keyword">string</span>.Format(<span class="string">"Image_{0}.{1}"</span>, user.Id, Path.GetExtension(model.DisplayImageUpload.FileName));</span><br><span class="line"> <span class="keyword">string</span> blobContentType = model.DisplayImageUpload.ContentType;</span><br><span class="line"> </span><br><span class="line"> AzureStorage.UploadFromStream(uniqueBlobName, blobContentType, model.DisplayImageUpload.InputStream);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> Ok();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net/">Asp.Net</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Azure/">Azure</a></li></ul>
</footer>
</div>
</article>
<article id="post-Extending-Asp-Net-Identity-2-0-with-custom-fields" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2014/10/31/Extending-Asp-Net-Identity-2-0-with-custom-fields/">Extending Asp.Net Identity 2.0 with custom fields</a>
</h1>
</header>
<div class="article-meta">
<a href="/2014/10/31/Extending-Asp-Net-Identity-2-0-with-custom-fields/" class="article-date">
<time datetime="2014-10-30T13:00:00.000Z" itemprop="datePublished">2014-10-31</time>
</a>
</div>
<div class="article-entry" itemprop="articleBody">
<p>Recently I needed a quick and low friction way of extending Asp.Net Identity 2.0, tying into the Entity Framework 6 way of working with custom fields and table mappings. To accomplish this first create something similar to a new ContosoIdentityUser which inherits from IdentityUser.</p>
<p>ContosoIdentityUser.cs</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.AspNet.Identity.EntityFramework;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">namespace</span> <span class="title">Contoso.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ContosoIdentityUser</span> : <span class="title">IdentityUser</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> FirstName { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> LastName { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> DateTime DOB { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> AddressLine1 { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> AddressLine2 { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Suburb { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> State { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Postcode { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line">} </span><br></pre></td></tr></table></figure>
<p>We then likewise create a new ContosoIdentityDbContext which inherits from IdentityDbContext.</p>
<p>ContosoIdentityDbContext.cs</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Data.Entity;</span><br><span class="line"><span class="keyword">using</span> System.Linq;</span><br><span class="line"><span class="keyword">using</span> System.Web;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNet.Identity.EntityFramework;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">namespace</span> <span class="title">Contoso.Web</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ContosoIdentityDbContext</span> : <span class="title">IdentityDbContext</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">ContosoIdentityDbContext</span>(<span class="params"></span>)</span><br><span class="line"> : <span class="title">base</span>(<span class="params"><span class="string">"DefaultConnection"</span></span>)</span><br><span class="line"> </span>{</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">OnModelCreating</span>(<span class="params">DbModelBuilder modelBuilder</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (modelBuilder == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException(<span class="string">"modelBuilder"</span>);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> SetIdentityUserModel(modelBuilder);</span><br><span class="line"> <span class="keyword">base</span>.OnModelCreating(modelBuilder);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">SetIdentityUserModel</span>(<span class="params">DbModelBuilder modelBuilder</span>)</span><br><span class="line"> </span>{</span><br><span class="line"> modelBuilder.Entity().Property(x => x.FirstName).HasMaxLength(<span class="number">50</span>).IsRequired();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.LastName).HasMaxLength(<span class="number">50</span>).IsRequired();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.DOB).IsRequired();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.AddressLine1).HasMaxLength(<span class="number">100</span>).IsRequired();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.AddressLine2).HasMaxLength(<span class="number">100</span>).IsOptional();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.Suburb).HasMaxLength(<span class="number">50</span>).IsRequired();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.State).HasMaxLength(<span class="number">3</span>).IsRequired();</span><br><span class="line"> modelBuilder.Entity().Property(x => x.Postcode).HasMaxLength(<span class="number">4</span>).IsRequired();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">} </span><br></pre></td></tr></table></figure>
<p>Now to stitch it all together in your boot strapper logic, in my case this is within Startup(), just wire up to our newly created extensions:</p>
<figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> userManager = <span class="keyword">new</span> UserManager(<span class="keyword">new</span> UserStore(<span class="keyword">new</span> ContosoIdentityDbContext()));</span><br><span class="line">UserManagerFactory = () => userManager;</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Asp-Net/">Asp.Net</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Security/">Security</a></li></ul>
</footer>
</div>
</article>
</section>
<footer id="footer">
<div class="outer">
<div id="footer-info" class="inner">
© 2019 Medic Consulting
</div>
</div>
</footer>
<script src="/js/jquery.min.js"></script>
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script src="/js/script.js"></script>
</div>
</body>
</html>