Code First Stored Procedures with Multiple Results
One of the most common request we get for Code First is to natively support stored procedures (sprocs). That item is right at the top of our backlog now – but it didn’t make it into EF5.
While using sprocs for insert, update and delete really isn’t possible with Code First yet, you can use DbSet.SqlQuery to fetch data using sprocs with your Code First model.
But what if you sproc has multiple result sets? I hadn’t tried this before but it turns out it’s actually really easy.
Assume we have the following sproc:
CREATE PROCEDURE [dbo].[GetAllBlogsAndPosts] AS SELECT * FROM dbo.Blogs SELECT * FROM dbo.Posts
The ObjectContext API has a Translate method that can be used to copy results from a DbDataReader into entities from our model. Fortunately we can drop down to the more complex ObjectContext API from our DbContext. If we provide Translate with an EntitySet name then it will add the entities to our context and track changes to them – the same as if they were retrieved using a LINQ query or the Find method.
Note: For Code First models the EntitySet name is the same as the name of the DbSet property on your derived context.
I could explain further… but a code listing is worth a thousand words:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Objects; namespace Sproc.Demo { class Program { static void Main(string[] args) { using (var db = new BloggingContext()) { // Make sure Code First has built the model before we open the connection db.Database.Initialize(force: false); // Create a SQL command to execute the sproc var cmd = db.Database.Connection.CreateCommand(); cmd.CommandText = "[dbo].[GetAllBlogsAndPosts]"; try { // Run the sproc db.Database.Connection.Open(); var reader = cmd.ExecuteReader(); // Read Blogs from the first result set var blogs = ((IObjectContextAdapter)db) .ObjectContext .Translate<Blog>(reader, "Blogs", MergeOption.AppendOnly); foreach (var item in blogs) { Console.WriteLine(item.Name); } // Move to second result set and read Posts reader.NextResult(); var posts = ((IObjectContextAdapter)db) .ObjectContext .Translate<Post>(reader, "Posts", MergeOption.AppendOnly); foreach (var item in posts) { Console.WriteLine(item.Title); } } finally { db.Database.Connection.Close(); } } } } public class Blog { public int BlogId { get; set; } public string Name { get; set; } public virtual List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public virtual Blog Blog { get; set; } } public class BloggingContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } } }
Summary
I don’t have anything else to say about stored procedures… but there are two things I love that this post reminded me of.
- Every time I have to use the ObjectContext API I am so glad we built the DbContext API. Do you know what an EntitySet is or what MergeOption does… probably not… and you shouldn’t need to just to copy results out of a DbDataReader.
I am glad that we built DbContext as a façade on ObjectContext though… so those obscure APIs are there when needed.
- Code First is awesome for blog posts (any many other things too)… you literally have everything you need to run my code sample by copying that code listing into Program.cs in your own console application. I could give you ‘a download of the completed solution’… but it just has Program.cs with that code in it – nothing else.



Very nice, thank you
Justin
September 3, 2012
[...] some new EF5 features in a 10min session for the Visual Studio launch. Rowan also blogged about calling stored procedures with multiple result sets in Code First and customizing ‘Reverse Engineer Code First’ in the EF Power [...]
Entity Framework Links #1 - ADO.NET Blog - Site Home - MSDN Blogs
September 25, 2012
[...] some new EF5 features in a 10min session for the Visual Studio launch. Rowan also blogged about calling stored procedures with multiple result sets in Code First and customizing ‘Reverse Engineer Code First’ in the EF Power [...]
Entity Framework Links #1 | MSDN Blogs
September 25, 2012
Can you give a rough estimate as to when there will be a release to natively support code first stored procedures. I’m in the beginning stages of building a large enterprise product and hoping that I don’t have to hand code stored proc wrappers for long. Thanks for all your hard work on this project!
Grady Dycus
October 4, 2012
It will be available in EF6, which will be RTM’d sometime next year. We haven’t started implementing the feature yet but it’s close to the top of the list now. I’d expect us to start working on it in the next couple of months. Once we have it implemented you could try it out using our nightly builds.
romiller.com
October 4, 2012
Just realized that what asked could be confusing, so to be a bit more specific, I’m looking forward to natively support for stored procedures, but I’m specifically hoping that “Reverse Engineer Code First” will generate the stored proc wrappers for me.
Grady Dycus
October 4, 2012
Hi Rowan,
Nice and helpful article. I think it is important to note that each entity (each blog/post) doesn’t get Translated or added to the DbSet.Local collection until the call is made to access one of it’s properties, such as in the foreach loops you have above.
Meaning that if you were to remove those foreach() {Console.WriteLine(…)} at the end of it, you have no entities in the Local collection of the DbSets.
Chris.-
Chris Amelinckx
January 10, 2013
The database surcuttre is similar to Northwind’s Employee and Territories, but I didn’t see how you configure the many-to-many mapping, how does it works , and could you explain more how to declare the many-to-many mapping? thanks.
Gora
February 13, 2013
Hello Rowan, I wonder if the Alpha has already a way to try stored procedures. Thank you for this example.
Richard Valdivieso
January 10, 2013
i tried yout example, it worked, but when i use a procedure with a parameter it dosent. Im doing that:
……
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = “[dbseg].[test]“;
cmd.Parameters.Add(new SqlParameter(“@test”, “this is a test”));
try
{
// Run the sproc
db.Database.Connection.Open();
var reader = cmd.ExecuteReader();
……
Lourenço
February 7, 2013
I am able to successfully do this with parameters on a stored procedure, two things to try:
1. Set the command type to stored procedure by doing
cmd.CommandType = System.Data.CommandType.StoredProcedure;
2. Remove the @ from the parameter name
Try them separately, particularly curious to see if (1.) solves it, because I seem to recall similar behavior when I first tried it.
Chris Amelinckx
February 7, 2013
It worked! But with the @test instead of just test, so it was the number 1 solution that solved it. i’ve tried the 2 solution but no good results alone and with cmd.CommandType = System.Data.CommandType.StoredProcedure, but it didnt worked. Only works if it have the @. thanks for the help.
Lourenço
February 7, 2013
I need to read many-to-many entities from procedure. Can you help me?
Anonymous
April 12, 2013
Hi Steve,How can I configure which datbaase to use? And where is the data saved by default?I have installed SQL Server CE 4.0. I added a datbaase called SimpleTest.sdf to the App_Data folder and then added a connectionstring to my Web.config with the same name as the DbContext that was scaffolded:When I run the application, I everything works, but my datbaase isn’t used. That means it’s storing the data somewhere else and I don’t know where or how to change this.I guess I’m just overlooking something very simple here
Etiene
April 23, 2013