Dev Blog - Johan Danforth
I'm Johan Danforth and this is my dev blog - a mix of .NET, ASP.NET, Rest, Azure and some other random coding stuff.
-
Build and Debug ASP.NET MVC 5 Framework Web Application 4.x in VS Code
Some of us are stuck with older, brownfield web projects based on Framwork 4.x that has to be supported yet for a while. But we’ve also tasted the sweetness of the still not bloated (dream on suckers, it will be bloated) VS Code and want to use it while it’s still pristine and fast. But is it possible?
Yes, it’s possible
It requires some editing with the launch.json and tasks.json files in the .vscode folder. Hang on and note that I just got this running last night.
Fist, open the folder where your “old” web application is located. In the same directory as your .sln file, create a folder named .vscode. In the .vscode folder, a file called tasks.json, which describes one or more build steps.
tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\MSBuild\\Current\\Bin\\msbuild.exe",
"args": [
"${workspaceFolder}/WebApplication1/WebApplication1.csproj",
"/t:build",
"/p:GenerateFullPaths=true",
"/p:Configuration=Debug",
"/consoleloggerparameters:NoSummary"
],
"group": {
"kind": "build",
"isDefault": true
}
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
]
}
Of course, you will have to replace “WebApplication1” with your project name, and you may also have to change the path to which ever msbuild.exe you want or need to run (bold, italic above).
To build i VS Code, press CTRL+SHIFT+B and your project should do just that. The task we defined is set to be the default, so VS Code will not ask you for anything, just build. Hopefully.
Run in IIS Express, 64-bit
To run this application in IIS Express, add the following task to tasks.json “tasks” array:
tasks.json (part)
… stuff before…
{
"label": "iisexpress",
"type": "shell",
"command": "C:\\Program Files\\IIS Express\\iisexpress.exe",
"args": [
"/path:${workspaceRoot}\\WebApplication1",
"/port:5006"
],
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
…stuff after…Again – replace “WebApplication1” with your project name.
Note that the iisexpress.exe we point at MUST be the 64-bit version, or we won’t be able to debug later on. The 64-bit version is located in c:\program files\ directory, and the 32-bit version in – you guessed it – the c:\program files(x86)\ directory.
To get IIS Express going from VS Code, press CTRL+SHIFT+P, to bring up the command prompt, then type/select “Tasks: Run Task” and select “iisexpress”.
You should see something like this in the VS Code terminal window (you may have to bring it up):
Starting IIS Express ...
Successfully registered URL "http://localhost:5006/" for site "Development Web Site" application "/"
Registration completed
IIS Express is running.
Enter 'Q' to stop IIS ExpressYou can now open a browser and point at http://localhost:5006, your web application should hopefully start.
Debugging
Debugging in VS Code is defined in the launch.json file. VS Code has to “attach” to the iisexpress.exe process. So create a launch.json file in .vscode directory, and add this:
launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Attach",
"type": "clr",
"request": "attach",
"processName": "iisexpress.exe",
}
]
}
To “attach” and debug our running application. Press F5 in VS Code. If all is well, the debugger should start running, and you should be able to set breakpoints and whatnot. I’ve not looked at how to debug *.cshtml files yet, but I’m sure it can be done if needed.
Build, Run and Browse
It’s possible to do build, run in iis express and start Chrome/Edge in one step for convenience. This is my full tasks.json file for that. On F5 it will run the default task “build run browse” which depends on the tasks “build”, “iisexpress” and “browser”. You might want to exclude the “browser” subtask, but here it is:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
//https://bigfont.ca/debug-an-iis-express-web-application-from-vs-code/
//appcmd add site /name:"WebApplication1" /bindings:https/*5006:localhost /physicalPath:C:\code-test\framework\WebApplication1\WebApplication1\obj\Debug\Package\PackageTmp
//"C:\Program Files (x86)\IIS Express\appcmd.exe" add site /name:"WebApplication1" /bindings:https/*5006:localhost /physicalPath:C:\code-test\framework\WebApplication1\WebApplication1\obj\Debug\Package\PackageTmp
"version": "2.0.0",
"tasks": [
{
"label": "build run browse",
"dependsOn": [
"build",
"iisexpress",
"browser"
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "build",
"type": "shell",
"command": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\MSBuild\\Current\\Bin\\msbuild.exe",
"args": [
"${workspaceFolder}/WebApplication1/WebApplication1.csproj",
"/t:build",
"/p:GenerateFullPaths=true",
"/p:Configuration=Debug",
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
{
"label": "iisexpress",
"type": "shell",
"command": "C:\\Program Files\\IIS Express\\iisexpress.exe",
"args": [
"/path:${workspaceRoot}\\WebApplication1",
"/port:5006"
],
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
{
"label": "browser",
"type": "shell",
"command": "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
"args": [
"http://localhost:5006"
],
"problemMatcher": []
}
]
}
Hopefully this might help some of you out there to use VS Code with Framework projects and transition over to .net 5 or 6. VS Code is still fast and really, really useful, though VS 2022 looks awesome and seems to outshine VS Code for me.
-
WebView2 Getting Started
The Microsoft WebView2 control allows you to embed Web Tech in native applications. It’s miles better than the old web browser controls. The WebView2 control is based on the Chromium platform.
The official docs for the control are found here: https://docs.microsoft.com/en-us/microsoft-edge/webview2/
Pre-reqs
Important note (as of 2021-01-20) - ensure you installed the following list of pre-requisites before proceeding:
- WebView2 Runtime or any non-stable Microsoft Edge (Chromium) Canary channel installed on Windows 10, Windows 8.1, or Windows 7.
- Visual Studio 2017 or later.
I suggest you visit https://www.microsoftedgeinsider.com/download and get the “Edge Canary Channel” that way.
Install WebView2 SDK in Visual Studio
Once you’ve created your WebForms or WPF project, install the Microsoft.Web.WebView2 package from Nuget:
PM> Install-Package Microsoft.Web.WebView2
Initializing WebView2 Control
Much about the control is asynchronous, and to make sure the control is loaded and ready, add an InitializeAsync() method to your form constructor, and place the events of interest there, like so:
public webView2TestForm() { InitializeComponent(); InitializeAsync(); } async void InitializeAsync() { webViewControl.NavigationCompleted += WebViewControlOnNavigationCompleted; webViewControl.WebMessageReceived += WebViewControlOnWebMessageReceived; await webViewControl.EnsureCoreWebView2Async(null); }
Send Message to Web Page
Easiest way to communicate between the web page and the native application is to send messages. From the native app:
webViewControl.CoreWebView2.PostWebMessageAsString("proxy.object.added");
To handle messages, the web page need some scripting:
window.chrome.webview.addEventListener('message', event => { console.log('Got message from host!'); console.log(event.data); handleMessages(event.data); });
Send Message to Host
To send a message from the web page
window.chrome.webview.postMessage('page.ready');
To handle messages from web page in the host:
private void WebViewControlOnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e) { var message = e.TryGetWebMessageAsString(); switch (message) { case "page.ready": Trace.TraceInformation("Got page.ready message!"); break; default: Trace.TraceWarning("Unknown message received: " + message); break; } }
Proxy Objects
One of the coolest features availeble is to send a “proxy object” from the native application to the web page. There are some limitations but powerful enough. The best info I’ve found is this: https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addhostobjecttoscript
The class/object must be exposed as a COM object (!):
[ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class ProxyHostObject { // sample property public string Name { get; set; } = "Johan"; // sample method public string GetName() { return Name; } // sample indexed property [System.Runtime.CompilerServices.IndexerName("Items")] public string this[int index] { get => _dictionary[index]; set => _dictionary[index] = value; } private Dictionary<int, string> _dictionary = new Dictionary<int, string>(); }
The use of the ClassInterface attribute is discussed in the Edge github repo, because the AutoDual value is not recommended in the docs, and even deprecated in dotnet:
Using AutoDual is strongly discouraged because of the versioning limitations described in System.Runtime.InteropServices.ClassInterfaceAttribute.
The issue is discussed here: https://github.com/MicrosoftEdge/WebView2Feedback/issues/517
To create and use the proxy object in the web page:
async function handleMessages(message) { switch (event.data) { case 'proxy.object.added': { const obj = window.chrome.webview.hostObjects.proxyobject; console.log(obj); var name1 = await obj.Name; console.log('name prop: ' + name1); var name2 = await obj.GetName(); console.log('name func: ' + name2); obj.GetName().then(name => { console.log("GetName promise name: " + name); }); // Indexed properties let index = 123; obj[index] = "test"; let result = await obj[index]; console.log(result); } break; default: console.log("unknown message: " + event.data); } }
-
How to setup Continuous Integration and Deploy Pipeline for ASP.NET MVC Framework application to Azure Web App
In this article I will try to explain in as few steps as possible a way to do CI and deploy from an Azure DevOps repository to an Azure hosted Web App. There are many ways to do this, but this worked for me (2021-01-18).
I’m creating the CI from the Azure portal, but you can also do it more manually, like described by Scott Hanselman in this blog post, which can be useful to read through: https://www.hanselman.com/blog/azure-devops-continuous-builddeploytest-with-aspnet-core-22-preview-in-one-hour
Tools and Frameworks used: Visual Studio 2019 Version 16.8.4, .Net Framework 4.7.2
Pre-reqs: I assume you already have an Azure Web App registered that runs ASP.NET 4.8 or similar. Mine is called ‘ci-test-pipeline’. I also assume you already have a repository and a project in Azure DevOps, again, mine is called ‘ci-test-pipeline’. You can also have your repo in github, the steps should be similar. In this repo, I have a vanilla ASP.NET MVC application created and checked in. In my case ‘ci.test.pipeline.web’ in a ‘ci.test.pipeline’ solution:
Initially the pipeline page in DevOps will be empty, unless you’ve already setup a pipeline. To create a CI pipeline you can go about it in a few different ways, but this article describes how to do it from the Web App page in Azure portal.
Goto the Deployment Center page in the Web App menu, and select to create a Continous Deployment (CI / CD) deployment for ‘Azure Repos’.
Then just follow the steps, it’s hard to do anything wrong as the wizard helps you with dropdowns along the way. Just make sure you select the correct repo and which ‘Web Application Framework’ you are using. In my case for an ASP.NET MVC .Net Framework solution, select ‘ASP.NET’.
Once the pipeline is created, it should look something like this on the deployment center page, with a message of a successful setup. This is still in the Azure portal, under your Web App:
If your project was already checked in, like in my case, this also triggers a first build + deployment in Azure DevOps:
And you should eventually get an email from Azure DevOps with a success or failure message. In my case I get a ‘partial success’ because of an error in the pipeline:
To view the deails of this message, just look through the detailed steps in the last run in Azure DevOps. You can easily get to these details by clicking the ‘View results’ link/button in the email, and drill into the job details:
For some reason the ‘Publish symbols path’ is missing a required task variable – ‘Build.SourceVersion’, but the build succeded and published/uploaded the artifact to a ‘drop container’ and the application is deployed to the Azure Web App. I’m not concerned about this warning right now, just happy it runs through.
To see the deployed files in the Azure portal, you can go look at the console development tool for the Web App in Azure portal, which is neat:
And you should also be able to view your website at <your web app name>.azurewebsites.net, to see that it works as expected:
To view and edit the pipelines steps created for you by the Azure portal wizard, visit the pipelines menu in Azure DevOps, and select to ‘Edit’ the pipeline:
In edit mode, you see all the steps, and you may opt to disable the ‘Test Assemblies’ and ‘Publish symbols path’, until they are needed:
Remember to save the changes, and try them out again. It’s useful to follow the pipeline job by clicking on the ‘Agent’ that runs the job. the screen updates for every step:
There are loads of different pipeline steps you can add and customize, which is very helpful if you want to do pre- or post-build steps with your code – like adding build information to your website - which build is deployed and what source code version is actually running.
That’s enough screenshots for this article. Hope it helps.
In a follow-up to this blog post, I’ll add build information to the website, and finally add authentication.
-
New features in Dapper.Contrib
I’ve given the official Dapper.Contrip package some love lately. So now all methods include support for lists! So instead of doing lots of insert calls in a loop, just call connection.Insert(myList); and Dapper/Contrib will interate and insert your entities effectively and fast!
Dapper.Contrib now contains these methods:
T Get<T>(id);
IEnumerable<T> GetAll<T>();
int Insert<T>(T obj);
int Insert<T>(IEnumerable<T> list);
bool Update<T>(T obj);
bool Update<T>(IEnumerable<T> list);
bool Delete<T>(T obj);
bool Delete<T>(IEnumerable<T> list);
bool DeleteAll<T>();For .NET 4.5 users there are also async methods available. For a complete and (almost) up-to-date help page on how Dapper.Contrib works, please visit https://github.com/StackExchange/dapper-dot-net/tree/master/Dapper.Contrib
Dapper.Contrib can be installed from Nuget, read more at https://www.nuget.org/packages/Dapper.Contrib/
-
Effectively insert list of objects with Dapper
If you are using Dapper as your sql/object mapper for .Net, you often find yourself inserting lists of objects. There are several ways of doing it, but maybe you're not doing it the fastest way possible (apart from hand-coding)?
What most people do is iterating over the list add execute the insert for each item like this:
foreach (var item in myList
{
connection.Execute("INSERT INTO MYTABLE VALUES (@A, @B)", item);
}but this is not the fastest way becuse the Dapper Execute() extension takes a list as parameter and iterates for you:
connection.Execute("INSERT INTO MYTABLE VALUES (@A, @B)", myList);
I tested by inserting 10.000 simple objects into an SqlCe database and clocked the resuts. The first option took 1.88 seconds, and the second took 1.19 seconds! So, letting Dapper iterate and insert is not twice as fast, but almost!
To install Dapper, run the following command in the Package Manager Console
PM> Install-Package Dapper
-
jQuery validate and the comma decimal separator
Oh this is such a simple and good solution that I must put it on my blog at least for my own future reference. Big THANKS to Lenard Gunda for writing this blog post
http://blog.rebuildall.net/2011/03/02/jQuery_validate_and_the_comma_decimal_separator
If you live outside the US and have problems with getting client side validation accepting comma decimal separator in input fields - just overwrite jQuery validation range and number methods with these javascript lines in the end of all your javascript loading.
$.validator.methods.range = function (value, element, param) {
var globalizedValue = value.replace(
","
,
"."
);
return
this
.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
}
$.validator.methods.number = function (value, element) {
return
this
.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
}
-
Bootstrap Image Carousel Fade Transition
The built in image carousel animation for Bootstrap is a slide, which is nice, but I found this fade transition while Googling, and it seems to work well. I found it here on http://codepen.io/Rowno/pen/Afykb and it’s penned by Roland Warmerdam.
Html:
<div class="carousel slide carousel-fade" data-ride="carousel"> <div class="carousel-inner"> <div class="item active"> <img class="img-rounded" src="..." alt="..."> </div> <div class="item"> <img class="img-rounded" src="..." alt="..."> </div> <div class="item"> <img class="img-rounded" src="..." alt="..."> </div> </div> </div>
Css (less):
.carousel-fade { .carousel-inner { .item { opacity: 0; -moz-transition-property: opacity; -o-transition-property: opacity; -webkit-transition-property: opacity; transition-property: opacity; } .active { opacity: 1; } .active.left, .active.right { left: 0; opacity: 0; z-index: 1; } .next.left, .prev.right { opacity: 1; } } .carousel-control { z-index: 2; } }
-
Formatting File Upload Input with Bootstrap
If you’re using file upload forms in your Bootstrap themed website and would like to format the button and the field nicely, take a peek at this article by Cory LaViska. He nails it.
A small example which produces an input-group like this (“Fil” means file in Swedish):
<div class="editor-label"> <label>Fil</label> <div class="input-group"> <span class="input-group-btn"> <span class=" btn btn-default btn-file"> välj fil... <input type="file" name="data" id="data"> </span> </span> <input type="text" id="valdfil" class="form-control" readonly /> </div> </div>
<script type="text/javascript"> $(document).ready(function () { $(document).on('change', '.btn-file :file', function () { var input = $(this), numFiles = input.get(0).files ? input.get(0).files.length : 1, label = input.val().replace(/\\/g, '/').replace(/.*\//, ''); input.trigger('fileselect', [numFiles, label]); }); $('.btn-file :file').on('fileselect', function (event, numFiles, label) { console.log(numFiles); console.log(label); $("#valdfil").val(label); }); }); </script>
You must also add these CSS lines:
.btn-file {
position: relative;
overflow: hidden;
}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
} -
Using FontAwesome in Universal Apps
Want to use FontAwesome in Universal Apps (both Windows Phone & Windows 8.1). The procedure is similar to how you do it for WPF:
1) Install-Package FontAwesome
2) Mark the file /fonts/fontawesome-webfont.ttf with “Build Action” set to “Content” (not “Resource”)
3) Try out the font like this in a sample Windows Phone main page:
<Viewbox> <TextBlock Text="" FontFamily="fonts/fontawesome-webfont.ttf#FontAwesome" /> </Viewbox>
And you should see:
Note that the steps and syntax is a bit different from how you do it in WPF.
-
Using FontAwesome in WPF
Want to use FontAwesome (http://fontawesome.io/) in your WPF application? With a few simple steps you can do it.
1) Use Nuget and Install-Package FontAwesome
2) Mark the file /fonts/fontawesome-webfont.ttf and set it’s “Build Action” to “Resource”
3) Test the font in a simple TextBlock like this:
<Window x:Class="FontAwesomeWPFBlogTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525"> <Grid> <Viewbox> <TextBlock Text=""
FontFamily="pack://application:,,,/fonts/#FontAwesome" /> </Viewbox> </Grid> </Window>Run it and you should see this:
The “hardest” thing is to make sure you enter the right icon-hexcode for the Text property. You can look at the different icons available in the icon-gallery page on http://fontawesome.io/icons/ then check the name of the icon you like. After that, go to the /Contents/font-awesome.css file in your project and look it up there, say for example the paint-brush icon:
.fa-paint-brush:before { content: "\f1fc"; }
The content-value says “\f1fc” which is the hex value of the paint-brush symbol in the font and that is the value you have to enter in the Text-property in your XAML:
Text=”&#f1fc;”