Phillip Kukel Phillip Kukel - 2 months ago 14
C# Question

C#/Entity Framework: locally add script to model

My team have read-access to a database maintained by a different team. We have a number of scripts that only we run, and so they've never been added as sprocs to the database (nor do we want them to be).

In Entity Framework 6, is it possible to include a file in my model which contains a stored procedure, so that we can leverage the code generation in EF?
We'd much rather have our code look like:

using (var db = new DatabaseEntities())
{
var properlyTypedResult = db.GetEntriesThatStartWith(firstName);
}


than:

using (var db = new DatabaseEntities())
{
var rawResult = db.Database.ExecuteSqlCommand("SELECT * FROM dbo.Person WHERE FirstName LIKE '@p0%'", firstName);
var properlyTypedResult = CastAppropriately(rawResult);
}


The functionality appears to be missing, but I thought I'd check regardless, I'd expect it to be in the designer view,
right-click, Add New -> Function Import...
... but this only allows adding sprocs to the model that are already in the database.

Answer

I couldn't find exactly what I was after. I decided to simply write my own code generation, and leverage as much of Entity Framework as I could.

  1. With query string in hand, execute against the database appropriately, using a SqlDataAdapter, with a DataTable

e.g.,

using (var context = new DbContext())    
{
    var dataTable = new DataTable();
    var connection = (SqlConnection)context.Database.Connection;
    if (connection != null && connection.State == ConnectionState.Closed)
        connection.Open();

    using (var adapter = new SqlDataAdapter(queryString, connection))
        adapter.Fill(dataTable);
}
  1. The DataTable contains the resulting column names along with all their types, now all we have to do is generate the code for the object.

i.e.,

var objectBuilder = new StringBuilder();
objectBuilder.AppendLine("public class QueryResult");
objectBuilder.AppendLine("{");
foreach (DataColumn column in dataTable.Columns)
{
    objectBuilder.AppendLine(String.Format("public {0} {1} { get; set; }", column.DataType.Name, column.ColumnName));
}
objectBuilder.AppendLine("}");
  1. Finally, create an extension method on the context object:

i.e.,

private static string GetQueryString(string firstName)
{
    return String.Format($"SELECT * FROM dbo.Person WHERE FirstName LIKE '%{firstName}%'");
}
public static partial class DbContextExtensions
{
    public static List<QueryResult> GetEntriesThatStartWith(this DbContext context, string firstName)
    {
        return context.Database.SqlQuery<QueryResult>(GetQueryString(firstName)).ToList();
    }
}

Now, we can use this as a regular sproc call:

using (var db = new DatabaseEntities())
{
    var properlyTypedResult = db.GetEntriesThatStartWith(firstName);
}
Comments