IronShay

Ironing code, geek t-shirts and even presentations!

NAVIGATION - SEARCH

Video, Slides and Code from my Session at aspConf 2012 – ASP.NET MVC Tips, Tricks and Hidden Gems

Last week I had the honor of taking part in the community-driven, ASP.NET-related, virtual event – aspConf 2012. My session was named ASP.NET MVC Tips, Tricks and Hidden Gems and it was generally about things I found to be important from my ASP.NET MVC experience – some were more basic and some were more hidden, too hidden some would say :)

I had lots of fun doing the session, and hopefully the attendees has fun too :)

A big big thanks to the aspConf crew – Javier Lozano, Jon Galloway, Eric Hexter, and friends – you guys did an AMAZING job! thanks!

Anyway, everything from my session is now on the interwebs – video, slides and code samples:

Video

Can be watched and downloaded on channel9: http://channel9.msdn.com/Events/aspConf/aspConf/ASP-NET-MVC-Tips-Tricks-and-Hidden-Gems

Slides

The slides are available on SlideShare: http://www.slideshare.net/shayfriedman/aspnet-mvc-tips-tricks-and-hidden-gems

 

Code Samples

All code samples from the session are available on my github page: https://github.com/shayfriedman/aspConf-mvc-tips-tricks-and-hidden-gems

That’s it. If you have any questions, let me know!
All the best,
Shay.



Mass Assignment Vulnerability in ASP.NET MVC

A couple of days ago the Ruby on Rails world got shocked by an old bug (or feature?) that could cause massive security issues sometimes. You can read about it here.

While reading about this vulnerability, I figured out that ASP.NET MVC worked in a very similar way… would it reproduce in an ASP.NET MVC environment? well, of course!

The Problematic Feature

ASP.NET MVC has this very convenient way of getting parameters from the request named Model Binding. The very simple example of Model Binding is controller actions with parameters. For instance:

public ActionResult Create(string name, string email)
{ 
  // ... do stuff ...
}

In this sample, the Model Binding feature will automatically fill in the name and email parameters with data from the request. Very similar to doing something like that:

public ActionResult Create()
{
  string name = Request["name"];
  string email = Request["Email"];

  /// ... do stuff ...			
}

This is already very helpful and it’s getting even better – you can set the parameter to a type of your own, and ASP.NET MVC will create an instance and fill it up for you. For instance, if you have a class named Person like this one:

public class Person
{
  public string Name { get; set; }
  public string Email { get; set; }
}

You can change the Create method to:

public ActionResult Create(Person person)
{			
  /// ... do stuff ...
}

By doing this, the Person.Name and Person.Email properties will automatically be filled in by ASP.NET MVC Model Binding.

OK, now that we understand what the essence of model binding is, let’s move on to the problem it represents…

Reproducing the Vulnerability

  1. Create a new ASP.NET MVC Application (I tried it with ASP.NET MVC 3 and 4).
  2. Add a new model class named User, as follows:
    public class User
    {
      public int Id { get; set; }
      public string Name { get; set; }
      public string Email { get; set; }
      public bool IsAdmin { get; set; }
    }
  3. Use the Add Controller dialog box to create a database context and a controller. Call it UsersController. Set the dialog properties as follows:
    Add Controller UsersController
  4. We don’t want the users to change the IsAdmin boolean value. It will be set somehow by the logics of the application later on. Therefore, open the Create.cshtml and Edit.cshtml views (they’re located under the Views/Users folder), and remove the IsAdmin part from them. The part to remove should look something like that:
    <div class="editor-label">
        @Html.LabelFor(model => model.IsAdmin)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.IsAdmin)
        @Html.ValidationMessageFor(model => model.IsAdmin)
    </div>

 

Now to the interesting part:

  1. Run the application and browse to /Users/Create
  2. Fill up the form and send. You’ve got a new user. IsAdmin is false.
  3. Go to the Edit page for this user. The URL will be something like /Users/Edit/1.
  4. Change the URL to /Users/Edit/1?IsAdmin=true and click enter to browse to it.
  5. Now click Save
  6. IsAdmin is now saved as True to the database. Oops.

This example is very very simple, but think about real world scenarios… this might get ugly. Very ugly. The biggest site that suffered the consequences of this vulnerability(based on Rails, but it’s the same thing) is GitHub – you can read their announcement here.

How to Defend

ASP.NET MVC offers a very simple solution to that problem – the Bind(Exclude=””) Attribute.  However, most people never use this feature. So… make a new habit from today – start using it. ALL THE TIME. And when I say ALL THE TIME, I mean that from now on you use it ALL THE F***ING TIME.

For my small sample, add [Bind(Exclude = "IsAdmin")] to the top of the model class (User.cs). After this change the model class should look like that:

[Bind(Exclude = "IsAdmin")]
public class User
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Email { get; set; }
  public bool IsAdmin { get; set; }
}

Rebuild and try our little hack again. It won’t work this time. Phew.

Stay safe,
Shay.



Razor Tip #2: Output Text in Code Context

Razor, the new ASP.NET MVC view engine is incredible. I like it. A LOT. Great work Microsoft!

In this series of posts I’m sharing some handy tips and tricks that can enhance your experience with this new view engine. Enjoy!

The Problem

Razor is the place where HTML and C# live together in harmony. This is, in my opinion, one of the things that make razor the great view engine that it is. However, there’s a fly in the ointment. Assume you want to output “Good Morning!” if the hour is between 6AM and 9AM:

Razor Tip #2

But oh no! Razor thinks “Good Morning!” is code!

The Solution

Razor has a special solution for that problem. Three different solutions for the matter of fact:

  • Writing the text inside an HTML element – razor knows to differentiate between C# and HTML so if you wrap the text with an HTML element, everything will work as expected:
    Razor Tip #2
  • Adding the “@:” symbol at the beginning of the line – using this symbol will tell razor that this line is an output line and should not be treated as code:
    Razor Tip #2
  • Using the special <text></text> element – when you want to output several lines within code context, it becomes irritating to use the “@:” symbol… For that you’ve got the special <text></text> element which tell razor that its content is to be outputted as HTML. Notice that the <text> element will not be outputted to the final HTML output.
    Razor Tip #2

All the best,
Shay.



Razor Tip #1: Explicitly Stating Statement Boundaries

Razor, the new ASP.NET MVC view engine is incredible. I like it. A LOT. Great work Microsoft!

In this series of posts I’m going to share some handy tips and tricks that can enhance your experience with this new view engine. Enjoy!

The Problem

You have to output a variable value and add some markup content immediately after it. For example, assume you have a variable holding a value representing a font size and you want to output it inside a style attribute, as follows:

image

But oh no! Razor can’t tell the difference between our fontSize variable and the “px” text that follows it so it will go look for a variable named “fontSizepx”!

The Solution

To solve the issue, we need to explicitly tell Razor where the variable name ends, or in more general terms – where the statement starts and ends. We do that by putting the variable name between parentheses:

image

You can also use the parentheses trick to output small code statements like these:

image 

All the best,
Shay.



Slides + Code Samples from my Session at SDC2011

Today I had the honor to talk before the Scandinavian crowd in Gothenburg at SDC2011. My session was “The Big Comparison of ASP.NET MVC View Engines” where I compared ASPX, Razor, Spark, NHaml and the StringTemplate view engines.

Thanks to everyone who attended! I hope you had as much fun as I had!

The code samples from the session can be downloaded from my github account: https://github.com/shayfriedman/AspNetMvcViewEnginesSamples

And here are the slides:

A recording of the session should be available on the conference site in a few days.

All the best,
Shay.



My Session at mvcConf 2–The Big Comparison of ASP.NET MVC View Engines

About a month ago I gave a session at the virtual mvcConf 2 event. My session was “The Big Comparison of ASP.NET MVC View Engines” where I talked about the differences between ASPX, NHaml, Spark and the Razor view engines.

The session was recorded and it is available on channel 9: http://channel9.msdn.com/Series/mvcConf/mvcConf-2-Shay-Friedman-The-Big-Comparison-of-ASPNET-MVC-View-Engines (at the time of this writing, it has more than 7,500 views! amazing!)

During the session I held a few polls about the preferences of the audience… the results were kinda interesting. These are screenshots of the results of the individual votes:

mvcConf 2 - The Big Comparison of ASP.NET MVC View Engines Poll #1mvcConf 2 - The Big Comparison of ASP.NET MVC View Engines Poll #2mvcConf 2 - The Big Comparison of ASP.NET MVC View Engines Poll #3

And summing all of them together, Razor got 70% of the votes, ASPX got 13%, Spark 12% and NHaml ended up with 6%:

image

So in conclusion, my prediction is that razor gonna conquer this asp.net mvc view engine market… However, the others will stay relevant and I recommend you to check them out. If they make you happier and more productive then go with them! do NOT hesitate.

Anyway, during the session I promised to post a detailed list of resources about the different view engines – so here it is:

Thanks to all the people who made mvcConf happen! and thanks to all attendees!

All the best,
Shay.



The Big View Engine Comparison – Razor vs. Spark vs. NHaml vs. Web Forms View Engine

One of the cool things about ASP.NET MVC (and other MVC web frameworks) is its capability to change the default view engine with a Microsoft or a 3rd-party one. ASP.NET MVC currently has a few options for view engine alternatives:

  • The web forms view engine – this view engine, with the ASP-like syntax, is the default one for ASP.NET MVC 1 and 2 applications.
  • Razor – the new view engine which will be the default one for ASP.NET MVC 3 applications.
  • Spark – an open-source view engine which aims to seamlessly integrate code and HTML.
  • NHaml – a port of the Ruby on Rails successful view engine named Haml. This open-source view engine aims to replace HTML tags with an easier to read and better organized syntax.

Note: Spark and NHaml are currently available for ASP.NET MVC 2. NHaml also doesn’t support .NET 4 at the moment. I believe that they will become available for version 3 as soon as it is out (moreover, they are open-source so you can help getting there!). Razor, on the other end, is not available on ASP.NET MVC 2 and below.

In this post I’ll go through the basic operations we do with our views and show you how to get them done with the different view engines mentioned above.

Displaying Variable Content

Here I’m comparing the way to present the content of ViewData["Message"] as an html-encoded string.

The Web Forms View Engine

<%: ViewData["Message"] %>

Razor

@ViewData["Message"]

Spark

${ViewData["Message"]}

 

NHaml

&= ViewData["Message"]

 

Conditions

Here I’m comparing the way of writing an if condition to display “<p>Party</p>” between 6AM and 9PM and “<p>It’s bed time</p>” between 9PM and 6AM.

The Web Forms View Engine

<% if (DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6)
   { %>
  <p>It's bed time!</p>
<% } else { %>
  <p>Party!</p>
<% } %>

 

Razor

@if (DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6) {   
  <p>It's bed time!</p>
} else {
  <p>Party!</p>
}

 

Spark

<if condition='DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6'>
  <p>It's bed time!</p>
</if>
<else>
  <p>Party!</p>
</else>

Another way of doing that in Spark (writing the condition inside the <p> tag):

<p if='DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6'>
  It's bed time!
</p>
<else>
  <p>Party!</p>
</else>

 

NHaml

- if (DateTime.Now.Hour > 20 || DateTime.Now.Hour < 6)
  %p= "It's bed time!"
- else
  %p= "Party!"

 

Loops

Here I’m comparing the way to declare a list in the view and create an <ul>-<li> HTML list out of it.

The Web Forms View Engine

<% var list = new List<string>() { "WebForms", "Razor", "Spark", "NHaml" }; %>
<ul>
<% foreach (var item in list) { %>
  <li><%: item %></li>
<% } %>
</ul>

 

Razor

@{ var list = new List<string>() { "WebForms", "Razor", "Spark", "NHaml" }; }
<ul>
@foreach (var item in list) {
  <li>@item</li>
}
</ul>

 

Spark

<var list='new List<String>() { "WebForms", "Razor", "Spark", "NHaml" }'/>
<ul>
  <li each='var item in list'>
    ${item}
  </li>
</ul>

 

NHaml

- var list = new List<String>() { "WebForms", "Razor", "Spark", "NHaml" }
%ul
  - foreach (var item in list)
    %li= item

 

Displaying a Form

Here I’m comparing the way to write a login form. I’m using the login form from the default ASP.NET MVC 2 template (located at Views/Account/LogOn.aspx). Notice that it is based on a model of type Demo.Models.LogOnModel (located inside Models/AccountModels.cs).

The Web Forms View Engine

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Demo.Models.LogOnModel>" %>

<% using (Html.BeginForm()) { %>
    <%: Html.ValidationSummary(true, "Login was unsuccessful.") %>
    <div>
        <fieldset>
            <legend>Account Information</legend>
                
            <div class="editor-label">
                <%: Html.LabelFor(m => m.UserName) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.UserName) %>
                <%: Html.ValidationMessageFor(m => m.UserName) %>
            </div>
                
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Password) %>
            </div>
            <div class="editor-field">
                <%: Html.PasswordFor(m => m.Password) %>
                <%: Html.ValidationMessageFor(m => m.Password) %>
            </div>
                
            <div class="editor-label">
                <%: Html.CheckBoxFor(m => m.RememberMe) %>
                <%: Html.LabelFor(m => m.RememberMe) %>
            </div>
                
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
<% } %>

 

Razor

@model Demo.Models.LogOnModel

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true, "Login was unsuccessful.")
    <div>
        <fieldset>
            <legend>Account Information</legend>
                
            <div class="editor-label">
                @Html.LabelFor(m => m.UserName)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(m => m.UserName)
                @Html.ValidationMessageFor(m => m.UserName)
            </div>
                
            <div class="editor-label">
                @Html.LabelFor(m => m.Password)
            </div>
            <div class="editor-field">
                @Html.PasswordFor(m => m.Password)
                @Html.ValidationMessageFor(m => m.Password)
            </div>
                
            <div class="editor-label">
                @Html.CheckBoxFor(m => m.RememberMe)
                @Html.LabelFor(m => m.RememberMe)
            </div>
                
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
}

 

Spark

<viewdata model="MvcApplication1.Models.LogOnModel"/>

# using (Html.BeginForm()) {
    ${Html.ValidationSummary(true, "Login was unsuccessful.")}
    <div>
        <fieldset>
            <legend>Account Information</legend>
                
            <div class="editor-label">
                ${Html.LabelFor(m => m.UserName)}
            </div>
            <div class="editor-field">
                ${Html.TextBoxFor(m => m.UserName)}
                ${Html.ValidationMessageFor(m => m.UserName)}
            </div>
                
            <div class="editor-label">
                ${Html.LabelFor(m => m.Password)}
            </div>
            <div class="editor-field">
                ${Html.PasswordFor(m => m.Password)}
                ${Html.ValidationMessageFor(m => m.Password)}
            </div>
                
            <div class="editor-label">
                ${Html.CheckBoxFor(m => m.RememberMe)}
                ${Html.LabelFor(m => m.RememberMe)}
            </div>
                
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
# }

 

NHaml

- using (Html.BeginForm())
  =Html.ValidationSummary(true, "Login was unsuccessful.")
    %div
      %fieldset
        %legend Account Information
        %div.editor-label
          =Html.Label("UserName")
        %div.editor-field
          =Html.TextBox("UserName")
          =Html.ValidationMessage("UserName")
        %div.editor-label
          =Html.Label("Password")
        %div.editor-field
          =Html.Password("Password")
          =Html.ValidationMessage("Password")
        %div.editor-label
          =Html.CheckBox("RememberMe")
          =Html.Label("RememberMe")
        %p
          %input{type="submit" value="Log On"}

 

Conclusion

This was a quick comparison of the most basic features of the web forms view engine, Razor, Spark and NHaml. As you could see, they all support the same things but each takes it to its own direction.

Which one did you like the most?

All the best,
Shay.

kick it on DotNetKicks.com Shout it



The Razor View Engine Basics

If you’ve been following me, you probably noticed that I became quite excited with the new view engine of ASP.NET MVC 3 – the razor view engine. If you’re familiar with it then you might agree or disagree with me, but if you don’t then this is a great time for you to make up your mind!

In this post I assume you are familiar with the concept of view engines. If not, please watch this before.

Let’s start.

At Sign (@) Galore

The razor view engine is all about the at sign (@). Unlike the inelegant <% %> signs of the common web forms view engine, here all you need is the at sign.
Basically, starting an expression with the at sign will lead to this expression being evaluated and output the result to the page. For example:

@DateTime.Now

This expression will end up printing the current date and time to the response stream. It is the equivalent of the following web forms view engine code:

<%: DateTime.Now %>
Note
At sign expressions are html encoded!
If you’re sure your output is OK and you don’t want it to be html encoded, you need to provide an IHtmlString object, or simply do:
@MvcHtmlString.Create("<b>BOLD!</b>")

By now some of you might be scratching your heads, moving anxiously in your sits or eating Ben & Jerry's uncontrollably… all of this because something is disturbing you with that line of code… something is missing…

It’s Magic! Magic!!!!!!11

Well, you were right! something is, indeed, missing… we only started the expression with the at sign, but we never told the framework where the expression ended!!! God help us all!!!

Do not worry. This is part of the charm of Razor – if your expression is a single call, there is no need to enclose it in some sort of way, you just write the expression after the at sign (and make sure it’s immediately after the at sign! for example, @ DateTime.Now will raise an exception).

Wait! But what about the times I wanna use blocks of code? or when I want to write out the result of 1 + 1? No problem, I have the solution for you! (how many times have you heard that before, right?)

So for simple expressions like additions (1 + 1, “Shay” + “ “ + “ Friedman”…) you enclose the expression in brackets. For example:

@(1 + 1)
@("Shay" + " " + "Friedman")
A Moment of Truth
What will happen if you write:
@"Shay" + " " + "Friedman"
?
... think for a moment ...
Answer: a "Parser Error" exception. Yes, get used to it.

Now, sometime you need actual code blocks like loops or conditions… and these brackets won’t help you there, son. And look carefully, because this is where it gets a bit… surprising.

Code Blocks and Razor Sitting in a Tree, K-I-S-S-I-N-G

Let’s start with the simplest use case – you want to set a variable within your view. How would you do that? @int a = 1; ? no no no. To do that, you enclose the code block within curly brackets, with an at sign at the beginning of course. For example, the next sample will output 1 to the page:

@{
    int a = 1;
}
@a

This was a very simple sample though… what about conditions for example? I’m glad you asked!

One way to write conditions (or any block of code), is by using the curly brackets solution:

@{
    string s = String.Empty;
    if (DateTime.Now.Year == 1980)
    {
        s = "Man you're so 80's!";
    }
    else
    {
        s = "It's the new millenium dude!";
    }
}
@s

This approach might become handy from time to time, but more commonly we’d want to write something out inside the condition. For these common occasions, you can use the next syntax:

@if (DateTime.Now.Year == 1980) {
    <p>Man, you're so 80's!</p>
} else {
    <p>It's the new millenium dude!</p>
}

Pay attention - it’s code and then html, and then code again, and then html again! Has your brain exploded yet?

So what happened here? in razor, everything blends together elegantly. The idea here is that razor understands when you write code and when you write html and can operate accordingly. This means that the next sample is valid as well:

@if (DateTime.Now.Year == 1983) {
    if (DateTime.Now.Month == 9) {
        <p>Wow, this month Shay is born!</p>
    } 
} else {
    <p>It's the new millenium dude!</p>
}

But this makes you ponder – is the next sample possible:

@if (DateTime.Now.Year == 1980) {
    Man, you're so 80's!
} else {
     It's the new millenium dude!
}

Though one might think it would be a valid razor syntax, it isn’t. This is because razor is smart, but it’s no genius… razor can’t tell if “Man, you’re so 80’s!” is code or html. Think of a sentence like “if pigs could fly” – is this a C# syntax error or plain text? because of this limitation, you have two options to do that – start the html part with html tags (like the previous sample that includes <p> tags) or use a <text> tag as follows:

@if (DateTime.Now.Year == 1980) {
    <text>Man, you're so 80's!</text>
} else {
     <text>It's the new millenium dude!</text>
}

Loops are very similar, too:

@{ var list = new List<string>() {"Razor","Rocks!"}; }
@foreach (var item in list) {
    <b>@item</b>
}

The Twitter Account Catastrophe

My twitter account is @ironshay, what’s yours? WAIT! WAIT!!!!111ONE all hell broke loose!!! TWITTER ACCOUNTS HAVE AN AT SIGN AT THE BEGINNING!!!!

That does it. If someone at Twitter thought about rewriting with ASP.NET MVC, now there’s no chance they’d go for it.

Hold you horses twitter! razor has a solution for you but you will have to write double at signs for that. For example, @@ironshay in a razor file will result in @ironshay in the response stream.

Here you go, Twitter can reconsider.

Read More!

This was a basic introduction to razor. If you liked what you read, I recommend you to read further and become the master of razor!

All the best,
Shay.

kick it on DotNetKicks.com Shout it



5 Reasons to be Excited about ASP.NET MVC 3

It’s already been 6 months since ASP.NET MVC 2 was released and version 3 is already taking shape. I, personally, am thrilled about that because this version seems to put ASP.NET MVC, in terms of maturity and functionality, on the first line along with other MVC frameworks out there like RoR.

I’ve put together 5 main reasons why I’m excited about this upcoming version. What about you?

1. The Razor View Engine

I said it before and I’ll say it again – the razor view engine is so cool!!! I really like the clean code I get when using it. In short, the razor view engine uses at signs (@) to indicate code blocks. For example, these are legit lines of code in razor:

@foreach (var item in Model) {
    <b>@item.FullName</b>
}

 

2. New Helpers

Helpers is an area ASP.NET MVC has been lacking at, compared to other MVC frameworks. MVC 3 will have new helpers that tend to close that gap:

  • Chart – helps creating charts. Very nice!
  • WebGrid – renders a collection of objects as a grid including paging and sorting. Should output a plain HTML table. Hopefully that’s all there is to it…
  • WebImage – helps in creating images. This also contains methods for basic image operations like rotating – cool!
  • WebMail – used to send emails.

The next helpers have been referred to as “task-based helpers”. They should have been a part of the beta version but they’re not there… I hope they’ll make it to the release version because they are just pure awesomeness:

  • Video – contains methods like Video.Flash, Video.Silverlight and Video.MediaPlayer which allow adding their corresponding HTML tags with ease.
  • Twitter – contains basic twitter operations like rendering the twitter stream for a specified user or the results of a given search.
  • ReCapcha – helps using the reCAPTCHA service.
  • Gravatar – helps consuming a user’s avatar from the Gravatar service.
  • Analytics – provides different methods like GetGoogleHtml, GetStatCounterHtml or GetYahooHtml that render the required javascript code for the different analytics services.

3. Support for Unobtrusive jQuery-Based Ajax

In ASP.NET MVC 3, Ajax.ActionLink, Ajax.RouteLink, Ajax.BeginForm and Ajax.BeginRouteForm will use JQuery to connect to the server. In addition, they will not use inline javascript, and instead they will call functions placed within javascript files.

Same thing applies to client-side validation.

4. Better Extensibility with new Dependency Injection Support

ASP.NET MVC 3 will contain numerous extensibility possibilities. Almost anything can be extended using DI, which opens so much opportunities. Just what extensibility means!

Read Brad Wilson’s post for a detailed guide to these new extensibility features.

 

5. NuPack Integration

NuPack is Microsoft’s new packaging system and it is fully integrated with ASP.NET MVC 3. Right click on the References folder in the Solution Explorer and choose “Add Package Reference…”:

The new Add Package Reference option

Now the NuPack UI will open and you can choose from the various different libraries on the online gallery:

NuPack

Pretty cool.

Well, these were my 5 reasons. So browse to http://go.microsoft.com/fwlink/?LinkID=191795, download the beta version and get excited too!

All the best,
Shay.

P.S. I’m not an ASP.NET MVP nor ASP Insider. Everything in this post was taken from various official blog posts and public web sites. Although the sources are as official as they can get (Scott Gu, Phill Haack…) this is only the beta version and as you know, in software – it ain’t over until the very last commit. Therefore, I recommend you check the release notes of every new version to see that no breaking changes were included.

kick it on DotNetKicks.com Shout it