When I pressed F5 to start debugging and Visual Studio found a compile-time error, nothing irritated me more than the dialog box Visual Studio popped up that asked, "There were build errors. Would you like to continue and run the last successful build?"
Let me be clear: No, I didn't want to run "the last successful build." I never wanted to run "the last successful build." Who in the world would want to run "the last successful build?" Like any other rational human being in the world, I wanted to run the version of the code with the changes I had just finished making ... well, after I fixed the compile errors, I mean.
So I turned that idiot message off.
If you also want to get rid of that message, then go to Tools | Options | Projects and Solutions | Build and Run. In the right-hand panel under "On Run, when build or deployment errors occur," change the selected item in the dropdown list to Do Not Launch. Now, when you have build errors, Visual Studio will just sit there. You'll have to get into the habit of checking your Error List to find out why you're not in debug mode but, for me, that didn't take me very long.
Posted by Peter Vogel on 05/08/2019 at 12:01 PM0 comments
Microsoft has some "best practice" advice to share on how to handle exceptions (a topic I've discussed elsewhere). The Microsoft article is well worth reading ... but there's one piece of advice that I disagree with (talk about hubris, eh?).
One of Microsoft's recommended practices is that you should prefer to throw a built-in Exception class rather than your own, custom Exception class. The problem I see with this is that the built-in Exception objects return, at best, the technical reason for the exception (for example, "Division by zero"). Creating your own exception object allows you to specify the business reason for the failure (for example, "Customer has no sales orders").
Personally, I think that the message with the business reason is more useful both to the user faced with the message and the developer who has to fix the problem. Instead, I recommend using a custom exception object and tucking the original Exception into your custom Exception object's InnerException property. That gives you the best of both worlds.
Posted by Peter Vogel on 04/26/2019 at 3:51 PM0 comments
In an earlier post, I discussed the three objects that Microsoft has provided for calling Web Services: HttpWebRequest, WebClient and HttpClient. At the time, I suggested WebClient was the simplest solution, unless you wanted to take advantage of HttpClient's asynchronous processing.
I've reconsidered that choice since then and I'm currently using HttpClient almost exclusively. Part of the reason is that HttpClient gives me the ReadAsAsync method. To understand why I like that method so much, you have to compare it to the alternatives.
Here's how to get a list of Customer objects out of the response from a Web Service using the traditional ReadAsStringAsync method:
HttpClient hc = new HttpClient();
HttpResponseMessage customersRm = await hc.GetAsync(url);
string customersString = await customersRm.Content.ReadAsStringAsync();
List<Customer> custs = JsonConvert.DeserializeObject<List<Customer>>(customersString);
Now here's the code using ReadAsAsync (even the method name is shorter!):
HttpClient hc = new HttpClient();
HttpResponseMessage customersRm = await hc.GetAsync(url);
List<Customer> custs = await customersRm.Content.ReadAsAsync<List<Customer>>();
The only problem is that you don't, in .NET 4.5 or later, get ReadAsAsync without some work -- you'll have to add the Microsoft.AspNet.WebApi.Client NuGet package to your project to pick up this extension method. I think that's worth doing.
Posted by Peter Vogel on 04/25/2019 at 8:58 AM0 comments
The biggest change in handling Views in ASP.NET Core is that you can now have more than one _Layout.cshtml file in your project and your Views will pick the one "closest to them." In ASP.NET MVC, you set a View's Layout property to the full path name of the Layout file to be used, like this:
Layout = "~/Views/Shared/_Layout.cshtml";
In ASP.NET Core, you can set the Layout property to as little as the name of the file. This, for example, is perfectly OK:
Layout = "_Layout";
When you don't provide a path name to your layout View, ASP.NET Core first goes looking for the layout in the same folder as the View that requested it. Only when ASP.NET Core doesn't find a file with the right name "locally" does ASP.NET Core go on to the /Shared folder to find a "global" layout View.
This means that you can set the Layout property in your _ViewStart.cshtml file to a default value "_Layout" but have Views in different folders pick up different layouts. This can mean a lot fewer Views having to override that default setting for the Layout property: If all the Views for a Controller share a layout View, you can just put that layout View in the Views folder for that Controller's Views.
Posted by Peter Vogel on 04/04/2019 at 9:39 AM0 comments
In an earlier column, I showed how to access configuration settings in your project's appsettings.json file and then make those settings available throughout your application as IOptions objects.
But you don't have to use the appsettings.json file if you don't want to -- .NET Core will let you hard-code your configuration settings or retrieve them from some other source (a database, perhaps). Wherever you get your settings from, you can still bundle them up as IOptions objects to share with the rest of your application. And your application will neither know nor care where those configuration settings come from.
Typically, you'll retrieve your settings in your project's Startup class (in the Startup.cs file), specifically in the class's ConfigureServices method. If you're not using appsettings.json, creating an IOptions object is a three-step process.
First, you'll need to define a Dictionary and load it with keys that identify your configuration settings and values (the actual settings). That code looks like this:
var settings = new Dictionary<string, string>
For my keys, I've used two-part names with each part separated by a colon (:). This lets me organize my configuration settings into sections. My example defined a section called toDoService with two settings: url and contracted.
The second step is to create a ConfigurationBuilder, add my Dictionary of configuration settings to it and use that ConfigurationBuilder to build an IConfiguration object. This IConfiguration object is similar to the one automatically passed to your ConfigureService method except, instead of being tied to the appsettings.json file, this one is tied to that Dictionary of settings.
Here's that code:
var cfgBuilder = new ConfigurationBuilder();
IConfiguration cfg = cfgBuilder.Build();
The fourth, and final, step is to add an IOptions object to your application's services collection from your own IConfiguration object:
Now, as I described in that earlier article, you can use that IOptions<toDoSettings> object anywhere in your application just like an IOptions object built from your appsettings.json file.
Posted by Peter Vogel on 03/26/2019 at 8:42 AM0 comments
In many of these tips, I've suggested ways that you might want to change Visual Studio's default configuration. That's not always a good thing. For example, I've known some developers who, because of some problem, had to re-install Visual Studio and lost all their customizations. I sometimes find myself at a client's site, working on a computer that isn't mine and looking foolish because some customization I depend on is gone ... or I used to, at any rate.
The solution to both problems is some preventative maintenance: Export your Visual Studio settings to a vssettings file. You can then restore those settings in the event of a disaster or moving to a new machine.
To export a file, from the Tools menu, select "Export selected environment settings" and then click the Next button. On the next page, by default, all settings are selected and that's the option I use (I'm concerned that if start picking and choosing settings, I'll leave one of my customizations behind). On this page, therefore, all I need to do is click the Next button.
The third and final page allows me to choose where I'll save the resulting vssettings file. I save it to one of my cloud drives so that I won't lose the file if something happens to my computer.
When I need to set up a new instance of Visual Studio, I import the file and get back my own, personalized version of my favorite development environment. When I'm working at a client's site, I first export the settings on the computer I'm using. I then import my vssettings file from my cloud drive (or a USB on one occasion when I wasn't allowed Internet access).
Posted by Peter Vogel on 03/21/2019 at 1:12 PM0 comments
By default, if you add a Razor Page to your project's Pages folder, the URL that you use to access that page is based on the Page's file name. So, for example, a Page with the file name CustomerManagement.cshtml is retrieved with the URL http://<server name>/CustomerManagement. There is an exception to this: A Page with the file name Index.cshtml is retrieved with just http://<server name>. This convention extends to subfolders: If that CustomerManagement.cshtml file is in the Pages/Customers folder then its URL is http://<server name>/Customers/CustomerManagement (and Index.cshtml in the same folder has http://<server name>/Customers as its URL).
I have two problems with this. First, the URL http://<server name>/Customers is the same for both a Page in a file named Customers.cshtml directly in the Pages folder and a Page in a file called Index in the Pages/Customers folder. You may say that this problem is one I've created for myself but I'd describe it as an accident waiting to happen ... and, when it does happen, it's a problem that can only be fixed by renaming or moving files. Of course, when you rename or move files, bookmarks all over the world stop working.
This is related to my second problem: Both the names of the files where I keep my code and their location on my Web server's hard disk should be private -- they're nobody's business but my own. Integrating file names and folder locations into your application's UI is the exact opposite of loose coupling, just like incorporating class and method names into your UI.
Fortunately, you have two options to implement loose coupling between URLs and Pages. The option I like is to use the page directive at the top of your Page's cshtml file: Just provide it with a string with an absolute URL that you want to use to access the page.
For example, this line, inside my CustomerManagement.cshtml file, means that the URL for my Page is now http://<server name>/Customers/Manage:
Alternatively, you can use the AddRazorPagesOptions method when configuring MVC support in your Startup class. This code also establishes http://<server name>/Customers/Manage as the URL for my CustomerManagement Page:
I need to point out, with either of these options, the RedirectToPage method must continue to use the Page's file name:
Posted by Peter Vogel on 03/15/2019 at 1:08 PM0 comments
I never get my code right the first time. And, even after my code passes all its tests, it's still not right. That's because I will have learned a lot about the problem when writing my code (wouldn't it be awful if that didn't happen?). But, unfortunately, much of my code reflects decisions made in an early, more ignorant stage of this learning process. As a result, I typically want to take some time, after the code passes its tests, to rewrite my code and make it "better."
The problem is that my clients need some proof that this rewrite is time well spent. One way to do that is to use Visual Studio's Analyze | Calculate Code Metrics menu choice to generate some hard numbers that show how the code is getting "better."
But, as I tell people all the time, no metric makes sense by itself: You need to compare your code's current numbers to what you had before to see if things are getting (in some sense of the word) "better." What you want to do is save your original numbers so you can compare them to your later, "better" numbers.
You have two ways to do this. One way is, in the Code Metric Results window, just select the metrics you're interested in, right-click on them and select Copy. Now you can paste these metrics into any place you want to keep them -- Excel would be a good choice. Of course, if you're doing that, why not just pick the Open List in Excel option on the Code Metric Results' toolbar? Now you can save those results in a workbook for later reference.
Heck, now that you've got those number in Excel, you can create a graph from them. My clients love graphs.
Posted by Peter Vogel on 03/13/2019 at 8:13 AM0 comments
You're thinking about making a change to that Transaction class but you're not sure how big an impact that change will have. One way to answer that question is to find all the places that the class is used.
Your first step is to click on the class name (and click on it in any place you find it, by the way). Then press Shift-F12 or right-click on it and pick Find All References. That opens a References window showing the statements that refer to your object (that window typically opens below your editor window).
As useful as that list of references is, I bet you really want to see the context of each of those lines to see how your object is used. Pressing F8 or Shift-F8 will take you to the "next" or "previous" reference; Double-clicking on any of the statements in the References list will take you directly to that statement.
Ctrl-Shift-Up Arrow and Ctrl-Shift-Down Arrow will also move you from one reference to another but, sadly, only within the current file.
Posted by Peter Vogel on 03/12/2019 at 12:15 PM0 comments
Eight-five percent of all application development is spent on existing systems, with existing databases. If you want to use Entity Framework's code-first development (where the database schema is an "implementation detail" generated from your object design) and migrations (which modifies your existing schema as your object model evolves), how do you do that with an existing database?
I'd suggest that you first step is to generate the object code that represents your existing tables (I use a tool for that). Once you've done that, and assuming you've used NuGet Manager to add Entity Framework to your project, you just need three commands to initialize your .NET Framework project for code-first migrations. Just enter these commands into Tools | NuGet Package Manager | Package Manager Console:
Add-Migration InitialCreate -IgnoreChanges
If you're working in .NET Core, you can skip the first command (Enable-Migrations). In .NET Core, migrations are enabled by default.
Posted by Peter Vogel on 03/11/2019 at 12:25 PM0 comments
The Dependency Inversion Principle says "the interface belongs to the client." As I've said elsewhere, adopting this principle means a reversing of the way applications used to be built: Design the database, build the objects to maintain the tables, wrap a UI around those objects and then bring the users in for training because they'd never figure the application out on their own.
The Dependency Inversion Principle says: Build the UIs that your users will understand (the interface belongs to them), design the objects that will make those UIs easy to build, build those classes, design the objects that make those classes easy to build and carry on until code-first Entity Framework generates the database you need.
You know this, already: the Dependency Inversion Principle is what drives the essential difference between ADO.NET and Entity Framework. With ADO.NET, it was your responsibility to create a connection (because you need a connection to the database), create a correctly configured command object (because you have to issue commands), call the appropriate execute method (because different SQL commands work differently) and then manage fetching rows and turning them into objects (because ... well, you get the picture). In other words, ADO.NET's API was driven by how the ADO.NET objects worked -- the reverse of the Dependency Inversion Principle.
On the other hand, essentially what Entity Framework says is, "Tell me what objects you want and I'll get them for you." Entity Framework provides the API that the application wants: An object-oriented way of retrieving, adding, updating and deleting data.
There are real cost-savings associated with the Dependency Inversion Principle. Because the principle requires that objects deliver the functionality that the client wants, interfaces tend to be more stable. Following the principle, APIs only change because the client program wants to do something differently (which, when you think about it, is the only reason we should be changing our code). You're welcome to upgrade how your objects work, of course ... but you're not allowed to change the API.
Of course, this level of abstraction isn't free: Entity Framework doesn't have the performance that pure ADO.NET has, even for the scenarios it targets: online, transactional applications. However, it does improve the productivity of developers who, let's face it (and given the current cost of hardware), are the most expensive part of an application -- just ask my client. And, when you do need "bare metal" levels of performance, there's always Dapper.
And, quite frankly, if you wanted the fastest performance, you'd be writing your code in assembler and running it on MS-DOS. Let's not be silly about performance.
Posted by Peter Vogel on 02/27/2019 at 10:55 AM0 comments
There are four Redirect help methods built into your .NET Core Controllers that you can use to tell a client that a resource exists ... but not at this URL. For all these cases, you should also be setting your response's Location header to tell the client where to find the result the client originally requested.
The helper methods and when to use them are:
- Redirect: This returns an HTTP 302 status code. This status code tells the client that what they requested can be found at the URL specified in the Location header of the response. However, that resource might be at this URL at some time in the future. If the original request was a POST, it's OK for the client to change that to a GET Request before using the new URL.
- RedirectPermanent: HTTP 301 status code. This code tells the client that the resource won't ever exist at this URL. The Location header should contain a URL that will give the client something like what they requested if a new request is made to that URL. For anything but GET requests, the user should be informed before a request is made to the new URL. As with Redirect, if the original request was a POST, it's OK for the client to change that to a GET Request before using the new URL.
- RedirectPermanentPreserveMethod: HTTP 308 status. This says that the requested resource won't ever exist at this URL. However, this code also says that, if this was a POST request, the new request to to the URL specified in the Location header must also be a POST request.
- RedirectPerserveMethod: HTTP 307 status. As with the original Redirect, this tells client that this redirect is temporary. As with the RedirectPermanentPreservice, this code also says that, if the original request was a POST request, the new request to the URL specified in the Location header must also be a POST request.
Posted by Peter Vogel on 02/25/2019 at 7:52 AM0 comments