Entity Framework Core, Calling Stored Procedures and Returning to a Model

Full source code available here.

I wrote a post some time back about calling a stored procedure with Entity Framework using the DbCommand, https://nodogmablog.bryanhogan.net/2016/07/entity-framework-core-and-calling-a-stored-proceduce/, but it was a bit complicated and not that easy to use.

There is now a FromSql method, you just pass the name of the stored procedure and the parameters. The method returns the results and maps them into a model that matches.

First step is to examine what the stored proc returns. I’m using the NorthWind database and its CustOrderHist stored procedure, its output looks like this – I know from the stored proc that this is a string and int.

I created a class to match –

public class OrderHistory
    private OrderHistory() { }

    public int Total { get; private set; }
    public string ProductName { get; private set; }

In Entity Framework Core 2 you can call the stored proc like this –

List<OrderHistory> orderHistoryList = await _northwindContext.OrderHistory.FromSql($"EXECUTE dbo.CustOrderHist {customerId}").ToListAsync();

In earlier versions you can use this –

List<OrderHistory> orderHistoryList = await _northwindContext.OrderHistory.FromSql("EXECUTE dbo.CustOrderHist {0}", customerId).ToListAsync();

Full source code available here.

Using an mdf file database with Entity Framework Core 2 in Visual Studio 2017

Full source code available here.

If you want to play around with Entity Framework it can be a little frustrating to create a complex database with a lot of sample data.
Instead, you could download the Northwind database from Microsoft, it has plenty of tables, a bunch of views and handful of stored procs. More than enough to try out many of the features of EF.

Now, all you have to is figure out how to get access to the db from your application, and it’s not as simple as you might hope.
So here’s how to do it in Visual Studio 2017.

Step 1
Download the Northwind database – you can get it here https://www.microsoft.com/en-us/download/details.aspx?id=23654
Install it, it will create some files in c:\SQL Server 2000 Sample Databases.

Step 2
Copy Northwind.mdf and Northwind.ldf to your user directory, something like c:\users\bryan. The Northwind files have to be in that directory to work with the connection string below.

Step 3
Now in Visual Studio use the following connection string.

"Data Source=(LocalDB)\\MSSQLLocalDB;DataBase=Northwind;Integrated Security=True;Connect Timeout=30"

My sample application is a .NET Core Web Api. In startup.cs, add the following –

public void ConfigureServices(IServiceCollection services)
	services.AddDbContext<NorthwindContext>(options =>
		options.UseSqlServer("Data Source=(LocalDB)\\MSSQLLocalDB;DataBase=Northwind;Integrated Security=True;Connect Timeout=30"));

If you want to place the database files elsewhere in your filesystem, add an absolute filepath to the connection string.

Step 4
Add an Order.cs class, this represents the orders table from the Northwind database. You can of course add more classes to represent other tables in the Northwind database.

public class Order
	public int OrderId { get; set; }
	public string CustomerID { get; set; }
	public int EmployeeID { get; set; }
	public DateTime OrderDate { get; set; }
	// etc.

Step 5
Add a NorthwindContext class that inherits from DbContext and add the Order DbSet.

public class NorthwindContext : DbContext
	public NorthwindContext(DbContextOptions options) : base(options) { }
	public DbSet<Order> Orders { get; set; }

Step 6
In the controller, add the NorthwindContext to the constructor.

public OrdersController(NorthwindContext northwindContext)
	_northwindContext = northwindContext;

Step 7
And finally, use the context in the Get method.

public async Task<IActionResult> Get(int orderId)
	var order = await _northwindContext.Orders.Where(o => o.OrderId == orderId).SingleOrDefaultAsync();
	return Ok(order);

Full source code available here.

Performing a WHERE IN with Entity Framework or on a List

WHERE IN is a very useful and commonly used feature of SQL, it looks like this –

SELECT * FROM ORDER WHERE OrderId IN (10248, 10249, 10250, 10251)

The will return up to four rows of data, showing just the orders that have an OrderId in the list you passed to the select statement.

You can do the same with Entity Framework by using the Contains predicate with a Where.

First you need to put the OrderIds you are looking for in some sort of enumerable.

IEnumerable ordersToFind = new List { 10248, 10249, 10250, 10251 };

Then you use Where on the Orders DbSet and check the orderIds against the list you just created.

var orders = _northwindContext.Orders.Where(o => ordersToFind.Contains(o.OrderId));

That’s all there is too it. You don’t have to use this on just Entity Framework, it also works on simple lists, arrays, enumerables, etc.

Unit testing Entity Framework Core Stored Procedures

Full source code available here.

Entity Framework Core has made unit testing CRUD functions much easier, see here for an example of using the In Memory Database, it allows you to search, add, remove and update rows.

But the in memory database doesn’t support execution of stored procedures in any way. I’ve seen people suggest that execution of a stored procedure is an integration test, but I don’t agree with that. If testing a method that does CRUD is a unit test, then testing a method that calls a stored procedure must also be unit a test.

An anonymous colleague of mine came up with this technique.

The first thing to do is add a class that implements IAsyncEnumerable and IQueryable.

public class SpAsyncEnumerableQueryable<T> : IAsyncEnumerable<T>, IQueryable<T>
	private IAsyncEnumerable<T> _spItems;
	public Expression Expression => throw new NotImplementedException();
	public Type ElementType => throw new NotImplementedException();
	public IQueryProvider Provider => throw new NotImplementedException();

	public SpAsyncEnumerableQueryable(params T[] spItems)
		_spItems = AsyncEnumerable.ToAsyncEnumerable(spItems);

	public IEnumerator<T> GetEnumerator()
		return _spItems.ToEnumerable().GetEnumerator();

	IEnumerator IEnumerable.GetEnumerator()
		return GetEnumerator();

	IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator()
		return _spItems.GetEnumerator();

Then add an extension class for DbSet, it’s DbSet because it has the method FromSql that calls stored procedures. MockFromSql takes a SpAsyncEnumerableQueryable of items that the stored proc will return.

public static class DbSetExtensions
	public static DbSet<T> MockFromSql<T>(this DbSet<T> dbSet, SpAsyncEnumerableQueryable<T> spItems) where T : class
		var queryProviderMock = new Mock<IQueryProvider>();
		queryProviderMock.Setup(p => p.CreateQuery<T>(It.IsAny<MethodCallExpression>()))
			.Returns<MethodCallExpression>(x =>
				return spItems;

		var dbSetMock = new Mock<DbSet<T>>();
			.SetupGet(q => q.Provider)
			.Returns(() =>
				return queryProviderMock.Object;

			.Setup(q => q.Expression)
		return dbSetMock.Object;

Finally, the unit test looks like this.

public async Task Get_Results()
	var products = new SpAsyncEnumerableQueryable<Product>(new Product()
		ProductId = Guid.NewGuid(),
		ProductName = "Some name",
		Size = 1,
		Value = 2
	}, new Product()
		ProductId = Guid.NewGuid(),
		ProductName = "Some other name",
		Size = 3,
		Value = 4

	var productContextOptions = new DbContextOptionsBuilder<ProductContext>()
		.UseInMemoryDatabase(databaseName: "Get results")

	ProductContext productContext = new ProductContext(productContextOptions);

	productContext.Products = productContext.Products.MockFromSql(products);

	ProductsController controller = new ProductsController(productContext);

	IActionResult actionResult = await controller.Get();

	OkObjectResult okObjectResult = actionResult as OkObjectResult;
	List<Product> productResponse = okObjectResult.Value as List<Product>;

	Assert.Equal(2, productResponse.Count);

Full source code available here.

Entity Framework Core 2 Unit Testing in .NET Core 2

Full source code available here.

Unit testing Entity Framework used to be quite a chore, but over the past few years it has become significantly easier.

In this post I’m going to show you how to use the InMemory database with named instances.

The ProductsController is fairly standard, it takes a DbContext in the constructor which is instantiated by dependency injection (see the Startup.cs in the source code if you don’t know how to do this).

public ProductsController(ProductsContext context)
    _productsContext = context;

The action methods use the context to query the database and return responses to the callers. From the response status code the caller knows if there is also content (products) in the response.

public async Task<IActionResult> Get()
    var products = await _productsContext.Products.ToListAsync();

    if (!products.Any())
        return NotFound();
    return Ok(products);

In the unit test project, (see here if you need help creating this), I’ve added a ProductsControllerTests class.

In my Get test I created an instance of a DbContextOptions by using the DbContextOptionsBuilder. On the builder I specify that I want an InMemoryDatabase and give it a unique name for this test – this is important, if the name is not unique across all tests or if it is left blank you will end up with a database shared by more than one test.

In the Arrange portion of the test I also instantiate the ProductsContext; then instantiate a few products, add them to the context; don’t forget to save the changes; finally create an instance of the of ProductsController.

Here is the full Arrange portion of the test.

// Arrange
var options = new DbContextOptionsBuilder<ProductsContext>()
.UseInMemoryDatabase(databaseName: "Get test")

ProductsContext productsContext = new ProductsContext(options);
Product product01 = new Product { Name = "first", ProductId = 1, Sku = "abc" };
Product product02 = new Product { Name = "second", ProductId = 2, Sku = "def" };

await productsContext.SaveChangesAsync();
ProductsController controller = new ProductsController(productsContext);

Next I Act, just calling the controller’s Get method. The controller already has the context and the get method will query it just like a real database.

// Act
var actionResult = await controller.Get();

Then I Assert. 

// Assert
var okObjectResult = actionResult as OkObjectResult;

List<Product> products = okObjectResult.Value as List<Product>;

Assert.Equal(2, products.Count());

Note the as OkObjectResult type, I know it's that type because that is what I return from the ProductsController.

I assign the value of the okObjectResult to a List.Assert that there are two entries in the list.

Testing NotFound - 404
In the ProductsController I also have a Get that takes a productId as an int and returns a product.
You should also test the scenario that no entries in the database match the search. In this case I am returning a NotFound, you may choose to return something else like an OK with nothing in it. In the case of a list, some people like to return an empty list; there is much debate on this topic and I have no interest in adding my two cents.

To test the NotFound response, the Act and Assert change a little. I call the GET method with an id that I know will return nothing.

// Act
var actionResult = await controller.Get(999);

// Assert
var notFoundResult = actionResult as NotFoundResult;

Assert.Equal(404, notFoundResult.StatusCode);

Full source code available here.