Skip navigation

I was recently developing a data driven project and used NuGet to install Entity Framework 6. It seems that it was recently released (and in fact replaced the option to download EF 5) so I went ahead and downloaded it. I had an existing database model that I wanted to connect to and synchronize with so I went through that process, which is essentially the same as the process used for EF5 and before. Before doing so however I did do some research and read about some breaking changes that come along with EF6, and while I wasn’t too concerned because I wasn’t necessarily upgrading a project from a previous version, it seems that some of those changes are still relevant to new projects.

Before I continue I will note that I haven’t reached out or done any serious research on the topic of the T4 templates as they’re provided with EF6, but I ran into some issues that after seeing the errors, I decided to resolve on my own. These problems may have been resolved through other means, or I may be doing something wrong. Either way I thought it would be good to write up these changes since I’ve had to make them on more than one occasion in my daily work.

Problems with the EF6 T4 Templates

1) By default the T4 templates are setup to generate classes with using statements that refer to the legacy Entity Framework .NET namespaces. With EF6 the System.Data.Entity.Objects and System.Data.Entity.Objects.DataClasses namespaces no longer exist. This seems to be an issue only when you have setup function imports (stored procedures) in your database model.

2) By default, the T4 templates are setup to generate classes with a single constructor that takes no parameters even though the base object (DbContext) provides others. This isn’t necessarily a problem per se, but if you’re getting configuration strings from places other than the default container, or you would like to change the connection string dynamically during runtime, you’ll need to enhance the default implementation.

3) After performing a refresh from the database model, some function imports may stop working. When this happens your project will compile fine but will generate a runtime error stating “The value of EntityCommand.CommandText is not valid for a StoredProcedure command. The EntityCommand.CommandText value must be of the form ‘ContainerName.FunctionImportName’.”

In all three cases the fixes require a simple change to the T4 template that is used to generate the classes from the EDMX file after the database mapping is completed. This T4 template can be found by expanding the files beneath the EDMX file in the Solution Explorer, and is generally named <NameOfYourModel>.Context.tt. For example if your EDMX file is called DataModel, you will find the T4 template underneath it and labeled DataModel.Context.tt.

You can resolve the first problem by locating the following block of code in the TT file and replacing it with the updated version listed below.

Original:

<# if (container.FunctionImports.Any()) { #>
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
<# } #>

Updated:

<# if (container.FunctionImports.Any()) { #>
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Core.Objects.DataClasses;
using System.Linq;
<# } #>

For the second problem, the resolution is also fairly simple and can be accomplished in much the same way. Locate the following code and replace it with the updated version below.

Original:

public <#=code.Escape(container)#>()
: this("name=<#=container.Name#>")
{
<# if (!loader.IsLazyLoadingEnabled(container)) { #>
this.Configuration.LazyLoadingEnabled = false;
<# } #>
}

Updated:

public <#=code.Escape(container)#>()
: this("name=<#=container.Name#>")
{}

public <#=code.Escape(container)#>(string connectionString)
: base(connectionString)
{
<# if (!loader.IsLazyLoadingEnabled(container)) { #>
this.Configuration.LazyLoadingEnabled = false;
<# } #>
}

As you can see here I’ve moved the logic for setting the lazy loading property to my new constructor. You may or may not need to set this up in this manner, but I did because the only constructor I’m consistently using is the new one I’ve created and I need to disable lazy loading in my situation.

To resolve the third problem, you will have to modify one parameter name to get the T4 template to generate import function methods correctly. Locate the following code and replace it with the updated version below:

Original:

public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction);

var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
if (includeMergeOption)
{
callParams = ", mergeOption" + callParams;
}

return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});",
returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
edmFunction.Name,
callParams);
}

Updated:

public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction);

var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
if (includeMergeOption)
{
callParams = ", mergeOption" + callParams;
}

return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});",
returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
edmFunction.FullName,
callParams);
}

Notice in the updated code, in the return data the property used from edmFunction is FullName instead of Name. This resolves the intermittent issue of updating the model from the database causing the runtime errors described.

I found these three fixes resolved compilation and runtime issues and also enabled me to generate connection strings dynamically based on user input at runtime versus by configuration from the app.config file. I think they are straight forward changes, but are somewhat of a pain to implement manually every time you create a new Entity data model. I would hope that these get integrated into future updates, or perhaps you might know how to permanently resolve these issues by some other means? Maybe there is a way to replace the template, since it seems like EF6 is still using the EF5 TT template on my system. I haven’t had a chance to look into it more deeply, so if you have please hit me up!

Leave a Reply

Your email address will not be published. Required fields are marked *