dotnet Exception Handling

.NET Exceptions – System.Data.EntityException

Moving along through the detailed .NET Exception Handling series, today we will be exploring the EntityException. The EntityException is the base exception class used by the EntityClient provider, which is part of the overall Entity Framework, which enables model-to-object mapping and relationships throughout ADO-based applications.

In this article we’ll examine EntityExceptions by looking at where it resides in the overall .NET exception hierarchy. We’ll also go over some functional C# sample code to illustrate the basics of working with the Entity Framework in .NET, which can be used to create, read, update, and delete SQL databases with ease. Let’s get started!

The Technical Rundown

All .NET exceptions are derived classes of the System.Exception base class, or derived from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

When Should You Use It?

Exploring the entirety of the Entity Framework is well beyond the scope of this article, but we need at least a basic understanding of it’s purpose and functionality to see how EntityExceptions may be thrown and captured. Traditionally, associating a physical object model (i.e. a row in a database table) with a logical object model (i.e. a programmatic instance of an object written in source code) would require manually creating and managing complex SQL statements.

On the other hand, the Entity Framework provides an easy means of mapping logical and physical database models to one another. Thus, with just a few extra lines of code, the Entity Framework can associate an existing logical model with the underlying data layer and its physical model representation. The framework will handle all “standard” SQL statements, database creation, and CRUD (create, read, update, delete) functionality.

To see this in action we’ve got a simple example. We start with a Book class, which is implements from the IBook interface and primarily consists of a few basic properties:

Critically, since the Entity Framework maps logical models like Book with a physical version within a relational database, our logical Book model needs some sort of identifying key. By default, Entity Framework will look for a property named [ClassName]Id (e.g. BookId). However, we don’t want to directly add a key to the Book class, so we’ll inherit it with the new BookEntity class, which we’ll use to add extra stuff just for this Entity Framework example:

The constructors are self-explanatory and they merely implement the base Book constructors. However, we’ve added two new properties of CompositeId and Id. Id is our primary key, which we specify with the KeyAttribute and DatabaseGeneratedAttribute. We want it to be an auto-incremented identity, so we also want to pass the DatabaseGeneratedOption.Identity argument to the DatabaseGeneratedAttribute. Just to illustrate the functionality of creating composite keys, the CompositeId dynamically retrieves its value by concatenating the Author and Title properties.

Now, to connect our logical BookEntity model to a physical model we need to create a new System.Data.Entity.DbContext instance that includes a DbSet<BookEntity> property:

Now, instantiating BookEntityContext allows us to perform all manner of functionality within our physical database model. To test things out we start with the AddBookEntityToContext(BookEntity book, BookEntityContext context) method:

As you can see, this method merely adds the passed BookEntity parameter to the passed BookEntityContext, then saves the new changes to the physical database model. Additionally, after making modifications, we’ll want to look at the database contents, so the OutputBookEntitiesOfContext(BookEntityContext context) method does this for us with a basic LINQ statement and log output:

Alright! Everything is setup, so let’s test this out in our Program.Main() method:

We start by creating a new instance of BookEntityContext. We then need to delete the underlying physical database (if it exists), since running this code multiple times would otherwise cause issues. We also then recreate the database after deleting it, so we start with a clean slate before adding data. Next, we call AddBookEntityToContext(BookEntity book, BookEntityContext context) a few times by passing in some books from the excellent Riftwar Saga series, before finally outputting the current data.

Executing this code results in the following output:

Cool! Everything worked just as expected. As we can see, our BookEntities were properly created, and our extra Id and CompositeId properties were populated as expected.

Now, you may be asking, “Where’s the database connection string?” In an effort to keep things as simple as possible out of the box, the Entity Framework defaults to trying to use localdb or SQL Express, if either is locally installed (which is usually the case when using modern versions of Visual Studio). In this case, we can connect to sqlexpress.Airbrake.Data.EntityException.BookEntityContext.dbo and are greeted with a BookEntities table that has all the appropriate columns and is populated with the three recently added books!

Since there are so many potential problems that could lead to EntityExceptions that we’ll just look at a simple example. Here we’ve added the UpdateBookPageCount(int id, int pageCount, BookEntityContext context) method:

This method attempts to update the page count of a BookEntity via its Id property. However, this implementation is poor, since we’re finding said matching book by iterating through all context.Books elements, rather than performing a LINQ query or similar. Still, it should get the job done, so let’s test it out:

Unfortunately, executing this code throws an EntityException at us, which includes an inner exception:

As indicated by the error, the issue is that we’re attempting to perform a new transaction, via context.SaveChanges(), while an active transaction thread is already occurring due to the foreach(var book in context.Books) iteration loop. In essence, we aren’t allowed to make changes to an element of an iterated collection while said iteration is still taking place.

To resolve this we’ll clean up the way we find a book by Id by using a simple query, as previously mentioned:

Executing this new version works as expected, adjusting the PageCount property to our database record for the BookEntity with Id of 1:

To get the most out of your own applications and to fully manage any and all .NET Exceptions, check out the Airbrake .NET Bug Handler, offering real-time alerts and instantaneous insight into what went wrong with your .NET code, along with built-in support for a variety of popular development integrations including: JIRA, GitHub, Bitbucket, and much more.