Scott Hanselman

Publishing an ASP.NET 5 app to Docker on Linux with Visual Studio

May 27, '15 Comments [19] Posted in ASP.NET | Docker | Open Source
Sponsored By

Docker Apps are mostly portableIt's early days, but this is a nice preview of the possibilities of things to come. I often use LEGO bricks in the way of an analogy when talking about software systems. I like the idea of choice, flexibility, and plug-ability. Choosing your language, operating system, deployment method and style, etc are all important.

There is a preview of an extension for Visual Studio 2015 (the release candidate at the time of this writing) that adds Docker support. If you have VS2015 RC you can get the Docker Extension here. You can certainly manage things from the command line, but I think as you go through this post you'll appreciate the convenience of this extension.

NOTE: It's also worth pointing out that there is a Windows client command line for Docker as well. You can "choco install docker" and read about it here.

The Brief What's Docker Explanation

If you aren't familiar with Docker, here's the super basics.

  • Virtual Machines: You likely know what a virtual machine is. It's the whole operating system, the whole computer, virtualized. If you have a 10 megabyte app you want to run, you may end up putting it in a 10 gigabyte virtual machine and carrying it around. That gives great security and isolation as your app is all alone on its own private VM, but it's a little overkill. Now you want to deploy 100 apps, and you've got space, CPU, and other things to think about. VMs also start slow and have to be actively maintained.
  • Docker/Linux Containers (and Windows containers "Docker for Windows Server"): Docker containers are sandboxes running on the same OS kernel. They are easy to deploy and start fast.  As a side effect of running on the same kernel, containers let you share most of that 10 gigabytes (as an example number) of support software between lots of apps, giving you less isolation but also using a LOT fewer resources. Containers start fast and the underlying shared resources are what's maintained and kept up to date.

Docker also is a way to package up an app and push it out in a reliable and reproducible way. So you can say that Docker is a technology, but also a philosophy and a process.

Docker will work on Windows and Linux

Docker and Visual Studio

Once you have the Docker for Visual Studio 2015 extension (preview) installed, go ahead and make an ASP.NET 5 app. Right click the project and hit Publish.

Publishing to Docker from Visual Studio

Note the Docker Containers section that's been added? You still have PAAS (Platform as a Service) and can also publish to VMs within Azure as well. Select Docker and you'll be here:

Selecting a Docker VM

We'll make a new VM to host our Docker stuff. This VM will have be the host for our containers. Today it'll be an Ubuntu LTS VM. Note that the dialog includes all the setup for Docker, ports, certs, etc. I could use existing VMs, of course.

Making a Docker VM

If you don't have a VM, then the initial create takes a while (5-10 min or more) so hang back. If you already have one, or the one you created is ready, then march on.

Visual Studio put in all our Docker details

Make special note of the Dockerfile option. You'll usually want to select your own manually created Dockerfile, assuming you're doing more than just a Hello World like I am.

The ASP.NET Dockerfile is up on GitHub: https://github.com/aspnet/aspnet-docker and in the Docker registry: https://registry.hub.docker.com/u/microsoft/aspnet/

In the build window you'll see lots of docker-related output. Here's a snipped version for flavor.

VERBOSE: Replacing tokens in Dockerfile: C:\Users\Scott\AppData\Local\Temp\PublishTemp\approot\src\WebApplication6\Properties\PublishProfiles\Dockerfile
VERBOSE: Package output path: C:\Users\Scott\AppData\Local\Temp\PublishTemp
VERBOSE: DockerHost: tcp://hanseldocker.cloudapp.net:2376
VERBOSE: DockerImageName: webapplication6
VERBOSE: DockerPublishHostPort: 80
VERBOSE: DockerPublishContainerPort: 80
VERBOSE: DockerAuthOptions: --tls
VERBOSE: DockerAppType: Web
VERBOSE: DockerBuildOnly: False
VERBOSE: DockerRemoveConflictingContainers: True
VERBOSE: LaunchSiteAfterPublish: True
VERBOSE: SiteUrlToLaunchAfterPublish:
VERBOSE: Querying for conflicting containers which has the same port mapped to the host...
Executing command [docker --tls -H tcp://hanseldocker.cloudapp.net:2376 ps -a | select-string -pattern ":80->" | foreach { Write-Output $_.ToString().split()[0] }]
VERBOSE: Building Docker image: webapplication6
Executing command [docker --tls -H tcp://hanseldocker.cloudapp.net:2376 build -t webapplication6 -f "C:\Users\Scott\AppData\Local\Temp\PublishTemp\approot\src\WebApplication6\Properties\PublishProfiles\Dockerfile" "C:\Users\Scott\AppData\Local\Temp\PublishTemp"]
VERBOSE: time="2015-05-27T10:59:06-07:00" level=warning msg="SECURITY WARNING: You are building a Docker image from Windows against a Linux Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories."
VERBOSE: Sending build context to Docker daemon 28.01 MB
VERBOSE: Step 0 : FROM microsoft/aspnet:vs-1.0.0-beta4
VERBOSE: vs-1.0.0-beta4: Pulling from microsoft/aspnet
VERBOSE: e5c30fef7918: Pulling fs layer
VERBOSE: e5c30fef7918: Pull complete
VERBOSE: e5c30fef7918: Already exists
VERBOSE: Digest: sha256:27fbe2377b5d4e66c4aaf3c984ef03d22afbfee3d4e78e10ff38cac7ff162d2e
VERBOSE: Status: Downloaded newer image for microsoft/aspnet:vs-1.0.0-beta4
VERBOSE: ---> e5c30fef7918
VERBOSE: Step 1 : ADD . /app
VERBOSE: ---> cf1f788321b3
VERBOSE: Removing intermediate container dd345cdcc5d9
VERBOSE: Step 2 : WORKDIR /app/approot/src/WebApplication6
VERBOSE: ---> Running in f22027140233
VERBOSE: ---> 7eabc0da4645
VERBOSE: Removing intermediate container f22027140233
VERBOSE: Step 3 : ENTRYPOINT dnx . Kestrel --server.urls http://localhost:80
VERBOSE: ---> Running in 4810324d32a5
VERBOSE: ---> e0a7ad38eb34
VERBOSE: Removing intermediate container 4810324d32a5
VERBOSE: Successfully built e0a7ad38eb34
The Docker image "webapplication6" was created successfully.
VERBOSE: Starting Docker container: webapplication6
Executing command [docker --tls -H tcp://hanseldocker.cloudapp.net:2376 run -t -d -p 80:80 webapplication6]
Docker container started with ID: 6d4820044df200e87f08cb5becbec879d1b58fcab73145ca3aa99a424c162054
To see standard output from your application, open a command line window and execute the following command:
docker --tls -H tcp://hanseldocker.cloudapp.net:2376 logs --follow 6d4820044df200e87f08cb5becbec879d1b58fcab73145ca3aa99a424c162054
VERBOSE: received -1-byte response of content type text/html; charset=utf-8
Executing command [Start-Process -FilePath "http://hanseldocker.cloudapp.net/"]
Publish completed successfully.

The interesting parts are the calls to dnx (the .NET Execution host), the warning that I started on Windows and I'm going to Linux, as well as the fact that we're using the "microsoft/aspnet" docker image.

ASP.NET in a Linux Docker Container

In my example, I had VS and the extension make my certificates. If I want to connect to this instance from the Windows Docker command line, I need to either pass those certs in, or set an env var. Here I'm running "ps" to see the remote docker containers in this Azure Linux VM. The Docker client looks in %USERPROFILE%\.docker for certs., so you just need to set DOCKER_HOST or pass it in like this.

C:\>docker --tls -H=tcp://hanseldocker.cloudapp.net:2376 ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d4820044df2 webapplication6:latest "dnx . Kestrel --ser 58 minutes ago Up 58 minutes 0.0.0.0:80->80/tcp silly_poincare

It worked great. Also be sure to explore the PublishProfiles folder that gets created in your Visual Studio project under "Properties." A PowerShell script and a Shell script get created in that folder that you can use to publish your app from the command line. For example:

.\hanseldocker-Docker-publish.ps1 -packOutput $env:USERPROFILE\AppData\Local\Temp\PublishTemp -pubxmlFile .\hanseldocker-Docker.pubxml

or from Linux:

cd ProjectFolder (like WebApplication/src/WebApplication)
source dnvm.sh
dnu restore --no-cache
mkdir ~/Temp
dnu publish . --out ~/Temp/ --wwwroot-out "wwwroot" --quiet
cd Properties/PublishProfiles
chmod +x ./Docker-publish.sh
./Docker-publish.sh ./Docker.pubxml ~/Temp/

I'm looking forward a cross-platform cross-tools choice-filled future. Finally, there's a great 7 part video series here called "Docker for .NET Developers" that you should check out on Channel 9.


Sponsor: Big thanks to Atalasoft for sponsoring the blog and feed this week! If your company works with documents, definitely check out Atalasoft's developer tools for web & mobile viewing, capture, and transformation. They've got free trials and a remarkable support team, too.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Introducing Visual Studio Code for Windows, Mac, and Linux

April 29, '15 Comments [89] Posted in ASP.NET | VS2015
Sponsored By
Screen Shot 2015-04-28 at 11.33.49 PM

What a wonderful time to be developer. I'm down here at the BUILD Conference in San Francisco and Microsoft has just launched Visual Studio Code - a code-optimized editor for Windows, Mac, and Linux and a new member of the Visual Studio Family.

Visual Studio Code (I call it VSCode, myself) is a new free developer tool. It's a code editor, but a very smart one. It's cross-platform, built with TypeScript and Electron, and runs on Windows, Mac, and Linux.

Visual Studio Code has syntax highlighting for dozens of languages, the usual suspects like CoffeeScript, Python, Ruby, Jade, Clojure, Java, C++, R, Go, makefiles, shell scripts, PowerShell, bat, xml, you get the idea. It has more than just autocomplete (everyone has that, eh?) it has real IntelliSense. It also as IntelliSense for single files like HTML, CSS, LESS, SASS, and Markdown. There's a huge array of languages that Visual Studio Code supports.

IMHO, the real power of this editor is its project IntelliSense for C#, TypeScript, JavaScript/node, JSON, etc. For example, when an ASP.NET 5 application is being edited in Visual Studio Code, the IntelliSense is provided by the open source projects Roslyn and OmniSharp. This means you get actual intelligent refactoring, navigation, and lots more. Visual Studio Code's support for TypeScript is amazing because it has JavaScript and TypeScript at its heart.

Visual Studio Code has git support, diffs, interesting extensibility models through gulp, and is is a great debugger for JavaScript and Nodejs apps. They are also working on debugging support for things like the .NET Core CLR and Mono on all platforms.

This a code-focused and code-optimized lightweight tool, not a complete IDE. There's no File | New Project or visual designers. If you live and work in the command line, you'll want to check free tool out.

You can download Visual Studio Code now at http://code.visualstudio.com.

They'll be blogging at http://blogs.msdn.com/b/vscode and you can email them feedback at vscodefeedback@microsoft.com and follow them at @code.

Download Visual Studio Code and check the the docs to get started. Also note the docs for ASP.NET support and Node.js support. Visual Studio Code is a preview today, but it's going to move FAST. It automatically updates and will be updating in weeks, not months.

And here's some screenshots of Visual Studio Code because it's awesome. Code what you like, how you like, on what you like, and you can run it all (by the way) in Azure. ;)

Screen Shot 2015-04-28 at 11.17.59 PM

Screen Shot 2015-04-28 at 11.28.35 PM

 
image

Have fun!


Sponsor: Big thanks to the folks over at Grape City for sponsoring the feed this week. GrapeCity provides amazing development tools to enhance and extend application functionality. Whether it is .NET, HTML5/JavaScript, Reporting or Spreadsheets, they’ve got you covered. Download your free trial of ComponentOne Studio, ActiveReports, Spread and Wijmo.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Proper benchmarking to diagnose and solve a .NET serialization bottleneck

February 20, '15 Comments [25] Posted in ASP.NET | Learning .NET | Open Source
Sponsored By

From http://adrianotto.com/2010/08/dev-null-unlimited-scale/Here's a few comments and disclaimers to start with. First, benchmarks are challenging. They are challenging to measure, but the real issue is that often we forget WHY we are benchmarking something. We'll take a complex multi-machine financial system and suddenly we're hyper-focused on a bunch of serialization code that we're convinced is THE problem. "If I can fix this serialization by writing a 10,000 iteration for loop and getting it down to x milliseconds, it'll be SMOOOOOOTH sailing."

Second, this isn't a benchmarking blog post. Don't point this blog post and say "see! Library X is better than library Y! And .NET is better than Java!" Instead, consider this a cautionary tale, and a series of general guidelines. I'm just using this anecdote to illustrate these points.

  • Are you 100% sure what you're measuring?
  • Have you run a profiler like the Visual Studio profiler, ANTS, or dotTrace?
  • Are you considering warm-up time? Throwing out outliers? Are your results statistically significant?
  • Are the libraries you're using optimized for your use case? Are you sure what your use case is?

A bad benchmark

A reader sent me a email recently with concern of serialization in .NET. They had read some very old blog posts from 2009 about perf that included charts and graphs and did some tests of their own. They were seeing serialization times (of tens of thousands of items) over 700ms and sizes nearly 2 megs. The tests included serialization of their typical data structures in both C# and Java across a number of different serialization libraries and techniques. Techniques included their company's custom serialization, .NET binary DataContract serialization, as well as JSON.NET. One serialization format was small (1.8Ms for a large structure) and one was fast (94ms) but there was no clear winner. This reader was at their wit's end and had decided, more or less, that .NET must not be up for the task.

To me, this benchmark didn't smell right. It wasn't clear what was being measured. It wasn't clear if it was being accurately measured, but more specifically, the overarching conclusion of ".NET is slow" wasn't reasonable given the data.

Hm. So .NET can't serialize a few tens of thousands of data items quickly? I know it can.

Related Links: Create benchmarks and results that have value and Responsible benchmarking by @Kellabyte

I am no expert, but I poked around at this code.

First: Are we measuring correctly?

The tests were using DateTime.UtcNow which isn't advisable.

startTime = DateTime.UtcNow;
resultData = TestSerialization(foo);
endTime = DateTime.UtcNow;

Do not use DateTime.Now or DateTime.Utc for measuring things where any kind of precision matters. DateTime doesn't have enough precision and is said to be accurate only to 30ms.

DateTime represents a date and a time. It's not a high-precision timer or Stopwatch.

As Eric Lippert says:

In short, "what time is it?" and "how long did that take?" are completely different questions; don't use a tool designed to answer one question to answer the other.

And as Raymond Chen says:

"Precision is not the same as accuracy. Accuracy is how close you are to the correct answer; precision is how much resolution you have for that answer."

So, we will use a Stopwatch when you need a stopwatch. In fact, before I switch the sample to Stopwatch I was getting numbers in milliseconds like 90,106,103,165,94, and after Stopwatch the results were 99,94,95,95,94. There's much less jitter.

Stopwatch sw = new Stopwatch();
sw.Start();

// stuff

sw.Stop();

You might also want to pin your process to a single CPU core if you're trying to get an accurate throughput measurement. While it shouldn't matter and Stopwatch is using the Win32 QueryPerformanceCounter internally (the source for the .NET Stopwatch Class is here) there were some issues on old systems when you'd start on one proc and stop on another.

// One Core
var p = Process.GetCurrentProcess();
p.ProcessorAffinity = (IntPtr)1;

If you don't use Stopwatch, look for a  simple and well-thought-of benchmarking library.

Second: Doing the math

In the code sample I was given, about 10 lines of code were the thing being measured, and 735 lines were the "harness" to collect and display the data from the benchmark. Perhaps you've seen things like this before? It's fair to say that the benchmark can get lost in the harness.

Have a listen to my recent podcast with Matt Warren on "Performance as a Feature" and consider Matt's performance blog and be sure to pick up Ben Watson's recent Book called "Writing High Performance .NET Code".

Also note that Matt is currently exploring creating a mini-benchmark harness on GitHub. Matt's system is rather promising and would have a [Benchmark] attribute within a test.

Considering using an existing harness for small benchmarks. One is SimpleSpeedTester from Yan Cui. It makes nice tables and does a lot of the tedious work for you. Here's a screenshot I stole borrowed from Yan's blog.

image11

Something a bit more advanced to explore is HdrHistogram, a library "designed for recoding histograms of value measurements in latency and performance sensitive applications." It's also on GitHub and includes Java, C, and C# implementations.

PercentileHistogramExample

And seriously. Use a profiler.

Third: Have you run a profiler?

Use the Visual Studio Profiler, or get a trial of the Redgate ANTS Performance Profiler or the JetBrains dotTrace profiler.

Where is our application spending its time? Surprise I think we've all seen people write complex benchmarks and poke at a black box rather than simply running a profiler.

Visual Studio Profiler

Aside: Are there newer/better/understood ways to solve this?

This is my opinion, but I think it's a decent one and there's numbers to back it up. Some of the .NET serialization code is pretty old, written in 2003 or 2005 and may not be taking advantage of new techniques or knowledge. Plus, it's rather flexible "make it work for everyone" code, as opposed to very narrowly purposed code.

People have different serialization needs. You can't serialize something as XML and expect it to be small and tight. You likely can't serialize a structure as JSON and expect it to be as fast as a packed binary serializer.

Measure your code, consider your requirements, and step back and consider all options.

Fourth: Newer .NET Serializers to Consider

Now that I have a sense of what's happening and how to measure the timing, it was clear these serializers didn't meet this reader's goals. Some of are old, as I mentioned, so what other newer more sophisticated options exist?

There's two really nice specialized serializers to watch. They are Jil from Kevin Montrose, and protobuf-net from Marc Gravell. Both are extraordinary libraries, and protobuf-net's breadth of target framework scope and build system are a joy to behold. There are also other impressive serializers in including support for not only JSON, but also JSV and CSV in ServiceStack.NET.

Protobuf-net - protocol buffers for .NET

Protocol buffers are a data structure format from Google, and protobuf-net is a high performance .NET implementation of protocol buffers. Think if it like XML but smaller and faster. It also can serialize cross language. From their site:

Protocol buffers have many advantages over XML for serializing structured data. Protocol buffers:

  • are simpler
  • are 3 to 10 times smaller
  • are 20 to 100 times faster
  • are less ambiguous
  • generate data access classes that are easier to use programmatically

It was easy to add. There's lots of options and ways to decorate your data structures but in essence:

var r = ProtoBuf.Serializer.Deserialize<List<DataItem>>(memInStream);

The numbers I got with protobuf-net were exceptional and in this case packed the data tightly and quickly, taking just 49ms.

JIL - Json Serializer for .NET using Sigil

Jil s a Json serializer that is less flexible than Json.net and makes those small sacrifices in the name of raw speed. From their site:

Flexibility and "nice to have" features are explicitly discounted in the pursuit of speed.

It's also worth pointing out that some serializers work over the whole string in memory, while others like Json.NET and DataContractSerializer work over a stream. That means you'll want to consider the size of what you're serializing when choosing a library.

Jil is impressive in a number of ways but particularly in that it dynamically emits a custom serializer (much like the XmlSerializers of old)

Jil is trivial to use. It just worked. I plugged it in to this sample and it took my basic serialization times to 84ms.

result = Jil.JSON.Deserialize<Foo>(jsonData);

Conclusion: There's the thing about benchmarks. It depends.

What are you measuring? Why are you measuring it? Does the technique you're using handle your use case? Are you serializing one large object or thousands of small ones?

James Newton-King made this excellent point to me:

"[There's a] meta-problem around benchmarking. Micro-optimization and caring about performance when it doesn't matter is something devs are guilty of. Documentation, developer productivity, and flexibility are more important than a 100th of a millisecond."

In fact, James pointed out this old (but recently fixed) ASP.NET bug on Twitter. It's a performance bug that is significant, but was totally overshadowed by the time spent on the network.

Thanks to Marc Gravell and James Newton-King for their time helping with this post.

What are your benchmarking tips and tricks? Sound off in the comments!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

OmniSharp - Making cross-platform .NET a reality, and a pleasure

November 27, '14 Comments [58] Posted in ASP.NET | Open Source
Sponsored By

In case you missed it, make sure to read Announcing .NET 2015 - .NET as Open Source, .NET on Mac and Linux, and Visual Studio Community because there's been some big stuff going on.

Here's the summary of the .NET 2015 Wave of awesomeness.

The other thing I wanted to talk about is a newly organized group of technologies called OmniSharp. Just to be sure there's no confusion, OmniSharp isn't a Microsoft project. While there are two Microsoft folks on the team of 8 or so, we are working on it as community members, not in an official capacity.

I "launched" this project in my talk at the Connect() mini-conference in New York a few weeks back. You can watch that video here on Channel 9 now if you like. However, the technologies around and under OmniSharp have been around for years...like over a decade!

As a team and a community we pulled together a bunch of projects and plugins, got organized, and created https://github.com/omnisharp and http://www.omnisharp.net. Jonathan Channon has a great overview blog post you should check out that talks about how Jason Imison created OmniSharpServer which is an...

HTTP wrapper around NRefactory allowing C# editor plugins to be written in any language. NRefactory is the C# analysis library used in the SharpDevelop and MonoDevelop IDEs. It allows applications to easily analyze both syntax and semantics of C# programs. It is quite similar to Microsoft's Roslyn project; except that it is not a full compiler – NRefactory only analyzes C# code, it does not generate IL code.

OmniSharp runs as its own process and runs a local Nancy-based web api that your editor of choice talks to. If you have an editor that you like to use, why not get involved and make a plugin? Perhaps for Eclipse?

We now have plugins for these editors:

  • Sublime
  • Brackets from Adobe
  • Atom from GitHub
  • Emacs
  • Vim

And these work on (so far) all platforms! It's about choice. We wanted to bring more than autocomplete (which is basically "I think you typed that before") to your editor, instead we want actual type-smart intellisense, as well as more sophisticated features like refactoring, format document, and lots of other stuff you'd expect only to see in Visual Studio.

We also brought in the Sublime Kulture package which gives Sublime users support for ASP.NET 5 (formerly ASP.NET vNext), so they can launch Kestrel (our libuv based local webserver), run Entity Framework migrations, and other arbitrary commands from within Sublime.

.NET in Sublime, in Vim, in Brackets, in Atom, and everywhere else, cross-platform

Here's OmniSharp running in emacs on my Windows machine. The emacs setup (here is an example) is a little more complex than the others, but it also gives emacs folks an extreme level of control. Note that I had to launch the OmniSharp server manually for emacs, while it launches automatically for the other editors.

image

Here is an ASP.NET MVC app running in Sublime. The Sublime OmniSharp package output can be seen in the debug console there (Ctrl+~ to see it).

image

OmniSharp is in very active development. We are looking at bringing in Roslyn, using the new ASP.NET Design Time Host, and improving robustness. It's not perfect, but it's pretty darn cool. There's lots of details in Jonathan's writeup with great animated gifs showing features. Also note that we have a Yeoman generator for ASP.NET that can get you started when creating ASP.NET 5 apps on Mac or Linux. The yeoman generator can create Console apps, MVC apps, and NancyFx apps.

You can get started at http://omnisharp.net.  See you there!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

ASP.NET 5 (vNext) Work in Progress - Exploring TagHelpers

November 20, '14 Comments [142] Posted in ASP.NET | ASP.NET MVC
Sponsored By

TagHelpers are a new feature of ASP.NET 5 (formerly and colloquially ASP.NET vNext) but it's taken me (and others) some time to fully digest them and what they mean.

Note that this, and all of ASP.NET 5 is a work in progress. TagHelpers can and will change. There is NO tooling support in Visual Studio for them, as they are changing day to day, so just be aware. That's why this post (and series is called Work in Progress.)

Historically we've used HtmlHelpers within a Razor View, so when you wanted a Label or a TextBox you'd do this. This is from the ASP.NET 5 Starter Web example.

<li>@Html.ActionLink("Home", "Index", "Home")</li>

There you have some HTML, then we start some C# with @ and then switch out. It's inline C#, calling a function that will return HTML.

Here's the same thing, using a TagHelper.

<li><a controller="Home" action="Index">Home</a></li>

The source for TagHelpers is (as with all ASP.NET source) up on GitHub, here. This is an anchor, A, so it'll be in AnchorTagHelper. The code is very simple, in fact, gets a collection of attributes and decides which to act upon.

In this case, "controller" and "action" are not HTML5 attributes, but rather ones that ASP.NET is looking for.

Question for You - Would you rather have these attributes and ones like them (including your own) be prefixed? Perhaps asp:controller or asp-controller? That's an open issue you can comment on! You could do [HtmlAttributeName("asp:whatever")] on a property or [TagName("foo")] for a tag if you liked.

How do these attributes get mapped to a TagHelper? Well, an attribute name is mapped directly to a C# property and automatically injected. See how AnchorTagHelper has public properties Action and Controller?

It's important to note that this isn't the second coming of WebForms controls, while the possible asp:foo syntax may look familiar (even though a prefix is optional.) This is more like syntactic sugar that gives you a compact way to express your intent. It doesn't give you any "control lifecycle" or anything like that.

Personally, I'd love to see them look different in the editor. For example, rather than

Tag Helpers

I'd like to see italics, or maybe a desaturation to show what's server-side and what's not, which will be super important if I'm NOT using a prefix to distinguish my attributes.

Tag Helpers, desaturated

The code below in this Before and After results in the same HTML and the same behavior. A nice aspect of TagHelpers it that you avoid the context switch from markup to C#.

Here is another example, a login partial form, before...

@using System.Security.Principal

@if (User.Identity.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
{
@Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li>
@Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })
</li>
<li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
</ul>
}
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
<li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}

and after...with the Microsoft.AspNet.Mvc.TagHelpers package added in project.json and then @addtaghelper "MyAssemblyName" in either your ViewStart.cshtml to get this in all views, or separately within a single view page.

@using System.Security.Principal

@if (User.Identity.IsAuthenticated)
{
<form method="post" controller="Account" action="LogOff" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a controller="Account" action="Manage" title="Manage">Hello @User.Identity.GetUserName()!</a>
</li>
<li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a id="registerLink" controller="Account" action="Register">Register</a></li>
<li><a id="loginLink" controller="Account" action="Login">Log in</a></li>
</ul>
}

This makes for much cleaner markup-focused Views. Note that this Sample is a spike that Damian Edwards has on his GitHub, but you have TagHelpers in the Beta 1 build included with Visual Studio 2015 preview or OmniSharp. Get involved!

Remember also to check out http://www.asp.net/vnext and subscribe to my YouTube Channel and this playlist of the ASP.NET Weekly Community Standup. In this episode we talked about TagHelpers in depth!

Related Posts

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Page 1 of 164 in the ASP.NET category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.