Limiting Your Entity Framework Models

If you're not reading Julie Lerman's Data Points column over at MSDN Magazine, you're missing out (in fact, I just realized that my recent column and her column are almost the same, except that hers covers more stuff). I especially appreciated her columns on Domain-Driven Design. And those columns got me thinking about where I should put my Entity Framework (EF) code.

The problem with code-first EF is writing out all the properties in all the entity classes and then applying the customizations those classes need. It's a lot of work: you don't want to do that more than once for any entity. And once you've got the entity built, its code may well be useful in other applications: you want to re-use.  It's tempting strategy to package up all of your entity code and your DbContext class in a class library that you can copy from project to project.

The problem with that strategy is that, over time, that class library is going to contain references to every table and relationship in your organization's database. Because of that, your program is going to stop dead the first time you issue a query or call SaveChanges, as EF gathers information about all those tables and relationships (I have a partial solution to that problem coming up in another tip).

But, as Julie suggests in her Domain-Driven Design columns, your applications should use bounded domains: your application should just be using the entities that your application needs. And that led me to think about how to share my EF code while avoiding the performance hit. I realized, for example, that the performance hit isn't related to how many entities I've defined -- it's related to how many entities I've referenced in my DbContext object.

It seems to me that the class library I'm copying from one project to another should contain just the code that defines my entities (i.e., I should think of it as a strategic resource). My DbContext object, on the other hand, should be defined in my application and just reference the entities my application needs (it should be a tactical resource).

There are obviously some wrinkles here. Navigation properties that reference other entities are one example: If you include one entity in your DbContext object, you'll need to include all of the entities those navigation properties reference… and pretty soon all your entities are back in your DbContext object. Another example: EF 6's custom configurations (see my column on using them to make Complex Types useful). Like your entities, those customizations are both a lot of work, and because they're part of an entity's definition, something that should be treated as a strategic resource.

For navigation properties, partial classes may be the solution. First, define the DbContext object in the application, referencing just the entities you need. Second, define the entities in partial classes in a class library that's shared among applications; where necessary, this class library will also hold methods to configure these entities (these methods will accept a ModelBuilder object). Third, in your application, add partial classes for the entities you're using, but use these classes to define the navigation properties your application needs (and only those properties). Finally: also in your application, in your DbContext's OnModelCreating method, call out to those methods that configure the entities you need. You'll be able to reuse your entities with their configurations and still have a bounded domain that will load as fast as it can.

This sounds promising. I'll try it out in my next project (provided that I can get my client on board, of course. Or maybe not: what they don't know won't hurt me).

Posted by Peter Vogel on 04/21/2014 at 9:10 AM0 comments


Stacking Up Error Handlers in ASP.NET

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/2014 at 7:43 AM0 comments


Using the 'Map Mode' Scroll Bar in Visual Studio 2013

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.

[Click on image for larger view.] Figure 1. The optional wider scrollbar in Visual Studio 2013 gives you a high-level view of your code file with a "peek" capability.

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/2014 at 7:16 AM0 comments


Free Tool: Entity Framework Power Tools

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/2014 at 7:18 AM0 comments


Defining Conversions for Visual Basic Classes

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/2014 at 7:51 AM0 comments


Creating a Default Error Handler for a Controller

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/2014 at 9:37 AM0 comments


Visual Studio Tip: Delete a Line

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/2014 at 9:17 AM0 comments


Adding Your Own Explicit Type Conversions in C#

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/2014 at 9:28 AM0 comments


Adding Your Own Implicit Type Conversions in C#

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/2014 at 6:44 AM0 comments


Using LINQ with Collections that Don't Support LINQ, Revisited

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/2014 at 3:23 AM0 comments


Progressive Server-side Validation by Group in ASP.NET

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/2014 at 7:45 AM0 comments


Version Control for Small Teams: Team Foundation Services

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/2014 at 9:16 AM0 comments


.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.