If you add multiple HandleError attributes to an ASP.NET MVC Controller method or class, each HandleError can target a specific exception and send the user a different View. The following example assigns custom Views to two exceptions (ArithmeticException and DivideByZeroException). For any other Exception, the user is sent the default Error View:
<HandleError(exceptiontype:=GetType(DivideByZeroException), Order:=99, View:="UserError")>
<HandleError(exceptiontype:=GetType(ArithmeticException), Order:=50, View:="ProcessError")>
<HandleError()>
Public Class MathController
The problem is that, once ASP.NET MVC finds a HandleError attribute with a matching Exception, MVC ignores the other HandleError attributes, and the default HandleError matches every exception. The code may throw a DivideByZeroException, but if ASP.NET processes the default HandleError attribute (the one that doesn't specify a type) first, ASP.NET will never find the more specific HandleError for the DivideByZeroException. For this plan to work, therefore, I need ASP.NET MVC to process the HandleError attributes in a specific order, beginning with the attributes for the more specific exceptions and going on to the more general ones.
That's where the Order property on the HandleError attribute comes in: if you don't set the Order property, the HandleError attributes are processed in an undetermined order. If you do set the Order property, ASP.NET MVC will process the HandleError attribute with the highest number in the Order property first. Any HandleError attribute whose Order property isn't set is processed after the attributes with their Order property set.
In my example, by giving the HandleError attribute for the DivideByZeroException an Order property with the highest value, I've ensured that it's processed before the HandleError attribute for the ArithmeticException. By not setting the Order property on the default HandleError attribute, I ensure that it's processed only after the more specific HandleError attributes. Just what I want.
Posted by Peter Vogel on 04/04/20140 comments
In Visual Studio 2013, right-clicking on the editor window's scroll bar and selecting Scroll Bar Options brings up a new option: "map mode for vertical scroll bar." The result is what you see in Figure 1: a wider scroll bar that shows your code in a compressed display.
Hovering your mouse over any part of the compressed code in the scroll bar gives you a preview of that part of the file. Clicking in the scroll bar moves you to that point in your code, instead of scrolling some arbitrary amount. And the bar still includes markers for errors and the caret (among other things), allowing you to jump to that part of your code by clicking on the marker.
I think that this feature is cool but, I've just started using it. And, besides, who cares what I think? If you're using Visual Studio 2013, try turning the feature on and leaving a comment about what you think.
Posted by Peter Vogel on 03/31/20140 comments
Regardless of what version of Entity Framework (EF) you're using, if you're doing code-first development, you should be looking at Microsoft's Entity Framework Power Tools, even if it is still in beta (Beta 4 appeared in October 2013 and added support for EF 6).
The beauty of code-first development is how easy it is to integrate your own code with the code required by EF. The problem with code-first EF development is the drudgery required to write all the code necessary to model your database. Power Tools goes a long way to eliminating that drudgery by writing the repetitive code for you -- all you have to do is install it through the Tools | Extension Manager menu choice.
To begin using the Power Tools, right-click on your project in Solution Explorer, select Entity Framework | Reverse Engineer Code First and you'll get the standard dialog for connecting to a database. Once you've connected to a database, Power Tools adds to your project the code for a DbContext class, plus the code for an entity class for each table in the database. Once you've generated that code, right-clicking on the file holding the DbContext class gives you access to more Power Tools magic. You should, for instance, be able to generate a read-only version of the database-first visual designer for your model.
I'd be lying if I didn't say that there are reasons that Power Tools is still in beta. Generating an entity class for every table is overkill (for any application I typically only need some of the tables in a database). I wish the Tools would let me use the configuration strings already set up in my project. And I'm not always able to generate the visual designer after generating my DbContext class.
If those “undocumented features” concern you (or you're just uncomfortable with using beta software), there's an alternative: You can use the latest version of EF's visual designer to
generate your code.
Posted by Peter Vogel on 03/26/20140 comments
Sometimes you have to repeatedly convert one of your custom classes into something else. In Visual Basic, your conversions come in two types: Widening conversions and narrowing conversions. In a widening conversion, the new object holds all of the data of the original object: no errors can occur in this conversion and no data will ever be lost. In a narrowing conversion, the new object holds only some of the original object's data: some data may be lost and errors may occur.
As an example, consider three objects: a TypicalCustomer that holds the data that 80 percent of your customers require, a PremiumCustomer adds some more data to support additional functionality, and a DeadbeatCustomer (which has the least amount of functionality) that holds less data. Converting a TypicalCustomer to PremiumCustomer would be a widening operation, while converting a TypicalCustomer to a DeadbeatCustomer would be a narrowing conversion.
To add the code to convert a TypicalCustomer object to either a PremiumCustomer or a DeadbeatCustomer object, you add an Operator member (not a Function or Sub) to the class for each conversion. The member must be called CType, accept the object that you're converting from, and return the object you're converting to. The member must also be declared as Public Shared, and flagged as either Widening or Narrowing. The code to add to a TypicalCustomer class to support converting it to a PremiumCustomer or a DeadbeatCustomer would look like this:
Public Class TypicalCustomer
Public Shared Widening Operator CType(ByVal Customer As TypicalCustomer) As PremiumCustomer
Dim pc As New PremiumCustomer
'transfer data from the Customer parameter to pc
Return pc
End Operator
Public Shared Narrowing Operator CType(ByVal Customer As TypicalCustomer) As DeadbeatCustomer
Dim dbc As New DeadbeatCustomer
'transfer some of the data from the Customer parameter to dbc
Return dbc
End Operator
End Class
Your CType method is automatically invoked when the appropriate conversion is required. If your code has Option Strict Off, the difference between a widening and narrowing conversions doesn't matter, as in this example:
Dim tc As New TypicalCustomer
Dim pc As PremiumCustomer
Dim dbc As DeadbeatCustomer
pc = tc
dbc = tc
But if you're using Option Strict On, narrowing conversions can only be performed using Visual Basic's CType function, as in this example:
dbc = CType(cm, DeadbeatCustomer)
Posted by Peter Vogel on 03/13/20140 comments
All you have to do to create a default error handler for your ASP.NET MVC controller is put the HandleError attribute on the class. Then, if your code throws an unhandled exception (one not caught in the Try…Catch block), the user will be sent the default Error View in the Views/Shared folder:
<HandleError>
Public Class HomeController
Inherits System.Web.Mvc.Controller
At least that's what the attribute will do if you have this tag inside the system.web element in your web.config file:
<customErrors mode="On" defaultRedirect="Error"/>
You can add whatever error information you want to the default Error View by adding code to the View that writes out properties from the Model's Exception property. Alternatively, setting the customErrors element's mode to RemoteOnly will, when you're debugging, let you continue to see ASP.NET's default error page with all of its information while still sending the Error View to your users in production.
Posted by Peter Vogel on 03/07/20140 comments
I've written before about how to comment out code. Sometimes, however, you just want to get rid of a line altogether. The good news is that deleting a line is just a keystroke away, and you don't have to select the whole line to delete the whole line.
Instead, you can leverage the L (for "L"ine) key: <Ctrl>_L cuts the line the cursor is on so that you can paste the line in somewhere else. The more deadly <Shift>_<Ctrl>_L completely deletes the line, but if you need to get the line back, you can still use undo. And if you want to delete (or cut) multiple lines, just select multiple lines before using <Ctrl>_L or <Shift>_<Ctrl>_L, and you still don't have to select whole lines.
Posted by Peter Vogel on 03/05/20140 comments
In my previous tip, I showed how to add an implicit conversion to C#. But there are two rules you should follow when defining an implicit conversion: 1) An implicit conversion should never throw an error, and 2) It should never lose information. If neither of those conditions is true, you should declare the conversion as explicit, as this code does:
public class PremiumCustomer
{
public string CustomerId {get; set;}
public string Name {get; set;}
public bool CanUseCredit {get; set;}
public static explicit operator PremiumCustomer(DeadbeatCustomer dbc)
{
PremiumCustomer pc = new PremiumCustomer();
pc.CustomerId = dbc.CustomerId;
pc.Name = dbc.Name;
if (pc.CanUseCredit != true)
{
pc.CanUseCredit = dbc.CanUseCredit;
}
else
{
throw new Exception("Can't convert DeadbeatCustomers with bad credit");
}
return pc;
}
}
The code to convert a DeadbeatCustomer to a PremiumCustomer would need an explicit cast and look, like this:
DeadbeatCustomer db = new DeadbeatCustomer();
PremiumCustomer pc = (PremiumCustomer) db;
Posted by Peter Vogel on 02/28/20140 comments
You may find that you have two classes that you frequently need to convert between. You could repeat the conversion code wherever you need it, or put it in some random class. Or you could add a new implicit conversion to one of the classes.
For instance, if you need to frequently convert a DeadbeatCustomer into a PremiumCustomer, just put the code inside a conversion method in the PremiumCustomer class that accepts a DeadbeatCustomer. To add a conversion method to a class, first define a public static method, followed by the implicit keyword, the operator keyword, and the method's return type. The method must accept a parameter of the type you want to convert. Here's the code for the DeadbeatCustomer to PremiumCustomer conversion:
public class PremiumCustomer
{
public string CustomerId {get; set;}
public string Name {get; set;}
public bool CanUseCredit {get; set;}
public static implicit operator PremiumCustomer(DeadbeatCustomer dbc)
{
PremiumCustomer pc = new PremiumCustomer();
pc.CustomerId = dbc.CustomerId;
pc.Name = dbc.Name;
pc.CanUseCredit = true;
return pc;
}
}
This code will automatically invoke your conversion method and handle the conversion:
DeadbeatCustomer db = new DeadbeatCustomer();
PremiumCustomer pc = db;
Be warned, though: I've made some assumptions here that I'll return to in my next tip.
Posted by Peter Vogel on 02/25/20140 comments
In an earlier tip, I discussed how the OfType operator lets you run LINQ queries against collections that don't otherwise support LINQ. One reader of that column pointed out that, under the hood, a function named Cast was doing all the work. Another reader pointed out that what's special about OfType and Cast is that, unlike the other LINQ keywords, they work with collections that aren't generic types. Instead, OfType and Cast allow you to specify the type of the objects in the collection.
As an example of how to use Cast and OfType, imagine an ArrayList holding nothing but Customer objects:
Dim customers As New ArrayList
customers.Add(New Customer)
customers.Add(New Customer)
Both Cast and OfType will let you issue LINQ queries against the collection:
Dim custs = From c In customers.Cast(Of Customer)()
Where c.CreditLimit = 0
Select c
Dim custs = From c In customers.OfType(Of Customer)()
Where c.CreditLimit = 0
Select c
But what if the ArrayList contains a variety of objects? This collection contains both DeadbeatCustomer and PremiumCustomers:
customers.Add(New DeadbeatCustomer)
customers.Add(New PremiumCustomer)
Either Cast or OfType will let you work with this collection of diverse objects, but in very different ways. OfType will extract the objects of a specific type, so this example will process only the DeadbeatCustomer objects in the collection:
Dim custs = From c In customers.OfType(Of DeadbeatCustomer)()
Where c.CreditLimit = 0
Select c
Cast, on the other hand, will let you work with all items in the collection, provided they share an interface (i.e., inherit from some common base object or implement a common interface). This example assumes that both DeadbeatCustomer and PremiumCustomer interfaces both implement an interface named IBuyer:
Dim custs = From c In customers.Cast(Of IBuyer)()
Where c.CreditLimit = 0
Select c
So thanks, guys: This was your column. I'll send you a bill for writing it up.
Posted by Peter Vogel on 02/04/20140 comments
I didn't know this until my fellow instructor at Learning Tree (and author of Learning Tree's ASP.NET Web Forms course), Kevin Rattan, pointed it out to me: when you trigger validation at the server by calling the Validate method, you can pass the name of a Validation Group to the method. When you do, only those Validators you've assigned to that group (through the Validators' ValidationGroup property) will execute.
This allows you to check parts of your page for problems, and if you find a problem, skip validating the rest of the page. If you're using the UpdatePanel, you can trigger validation just for the controls in the panel that was posted back to the server.
Posted by Peter Vogel on 01/31/20140 comments
In an earlier tip, I suggested that in five or 10 minutes, you could set up a source control system for yourself by using Subversion; but what if you're part of a team? Even if every team member has their own source control in place (doubtful), you still want some central repository that represents your "gold" code. There is a simple solution and it's free, provided your project has five or fewer members: Team Foundation Service (Team Foundation Server in the cloud). While the TFS team says TFS will work with any editor, TFS integrates especially well with Visual Studio 2012 and 2013. If you're using Visual Studio 2010, you can still use TFS: just follow these steps, which includes installing a quick patch.
With TFS, you not only get cloud-based source control, you also get a central location to keep track of your team's work items (tasks and features), plus virtual meeting rooms if you're not all within driving distance of each other. Of course, if you get too dependent on TFS and add a sixth member to your team, you might have to buy one of Microsoft's plans or export your code and find a new provider.
Posted by Peter Vogel on 01/27/20140 comments
A surprising number of Visual Basic programmers (i.e., more than one) aren't aware of a feature unique to their language: Project-level Imports. Namespaces added to the list of project-level Imports are, effectively, automatically added to the code files that make up your application --there's no need to add Import statements to the top of the code file for those namespaces.
You can add or remove project-level Imports by opening your Project's properties (right-click on the project in Solution Explorer and select Properties) and selecting the References tab. The list of project-level Imports appears at the bottom of the resulting form. Adding a namespace there means that you don't have to include it in your code file.
There is a wrinkle with using project-level Imports, however: If you remove the libraries whose namespaces are referenced in the Imports, your application will stop compiling. You'll need to track those Imports down and delete them. The good news here is that, instead of those Imports being scattered through your code files, they're all in one place -- though, perhaps, not an obvious place.
Posted by Peter Vogel on 01/23/20140 comments