webdad3 webdad3 - 3 months ago 39
C# Question

T4 - Entity Framework Error: Method not found: 'System.Data.Entity.DbSet`1

Trying to create a fairly simple T4 which I'm trying to load some values that I can use for later:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="c:\users\<username>\documents\visual studio 2015\Projects\2_DataModelGenerator\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll" #>
<#@ assembly name="c:\users\<username>\documents\visual studio 2015\Projects\2_DataModelGenerator\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll" #>
<#@ assembly name="c:\users\<username>\documents\visual studio 2015\Projects\2_DataModelGenerator\2_Data\bin\Debug\2_Data.dll" #>

<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="2_Data" #>

<#@ output extension=".cs" #>

<#
DevEntities dbContext = new DevEntities();

var labelClassNames = (from sd in dbContext.tblDatas
where sd.SID == 155
select new
{
SID = sd.SID.ToString(),
SValue = sd.SValue.ToString()
}).ToList();

#>


When I save the T4 I get this error:


Error Running transformation: System.MissingMethodException: Method
not found: 'System.Data.Entity.DbSet`1<2_Data.tblData>
2_Data.DevEntities.get_tblDatas()'


I have tested this code in another solution (using EF 6.1.3) and it works without issue (loads data into
var labelClassNames
). Seems to be an issue only in T4 template.

Do I need to import another namespace? I'm not sure what I'm missing!

Update:

In my TT code:

dbc context = new dbc();
DevEntities mdc = new DevEntities();
mdc = context.returnContext();


Added a class in my class library:

public class dbc
{
private DevEntities dbContext;
public dbc()
{
dbContext = new DevEntities();
}
public DevEntities returnContext()
{
return dbContext;
}
}


Same error as before:


Running transformation: System.MissingMethodException: Method not
found: 'System.Data.Entity.DbSet


However, when I debug the t4 template I get a different message:


No connection string named 'DevEntities' could be found in the
application config file.


However, I have copied the App.config file from my class library that include the .edmx file down to my project that contains the .tt file

Any ideas?

Answer

The classes in the same project of T4 template is not visible by it, and to simplify its debugging, the following is a step by step to run T4 successfully in EntityFramework without problem and ease of debugging:

step 1: Create a separate class library project for the model

Create a separate class library project for the model, to be referenced by your application and T4 template.

That also simplify unit test and resolve many problems with T4.

Step2 : resolving Connection String

To avoid the problems of configuration file and configure the connectionString correctly, create a partial class which extend the Context class to avoid overwriting when re-generate the context. Define new overload constructor.

for example, NorthwindEntities

      using System.Data.Entity;
     namespace NorthWin
     {        
        public partial class NorthwindEntities : DbContext
        {
            public NorthwindEntities(string connString)
                : base(connString)
            {
            }
        }
    }

step3: Create all DAL methods in separate class(s)

All methods that you need in T4 template,create it in separate class(es) in the same class library project and call them from T4 template.

Define the connectionString explicitly in your class (note the single quote in the connection string). Edit: or you can use the include template. example:

    using System.Linq;
    namespace NorthWin
    {
        public class DAL
        {

            string ConnectionString = @"metadata=res://*/NorthWind.csdl|res://*/NorthWind.ssdl|res://*/NorthWind.msl;provider=System.Data.SqlClient;provider connection string='data source=myserver;initial catalog=Northwind;persist security info=True;user id=xxx;password=yyy;MultipleActiveResultSets=True;App=EntityFramework';";

   public DAL (string connString)
    {
      ConnectionString =connString;
    }       
    public int GetCustomerCount()
            {
                var n = 0;
            // call ye new overload constructor
                using (var ctx = new NorthwindEntities(ConnectionString))
                {
                    n = ctx.Customers.Count();
                }
                return n;
            }
        }
    }

step 4: build your T4 Define the minimum assemblies for EntityFramework and Call DAL methods

example

    <#@ template debug="false" hostspecific="true" language="C#" #>
    <#@ output extension=".txt" #>

      <#@ assembly name="System.Xml"#>
    <#@ assembly name="$(TargetDir)NorthWin.dll" #>
    <#@ assembly name="$(TargetDir)EntityFramework.dll" #>
    <#@ assembly name="$(TargetDir)EntityFramework.SqlServer.dll" #>
    <#@ assembly name="EnvDTE" #>
    <#@ assembly name="System.Configuration" #>

    <#@ import namespace="System.Configuration" #>
    <#@ import namespace="System" #>
     <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Collections.Generic" #>
     <#@ import namespace="NorthWin" #>
    <#

       int n=0;
        var dal = new DAL();
       / /call DAL methods
        n= dal.GetCustomerCount();

     #> 

    <#= n #>
    {
      ... // More code here. 
    }

Output of T4 Template:

114
{
  ... // More code here. 
}

Edit:

You can get the connectionString from configuration file using the include file as described in:

Injecting Your Web.Config Connection String Into Your T4 Template

then you pass the connectionString to your DAL classes with constructor that accept connection String.

in your template add this code

<#@ include file="ConfigurationAccessor.ttinclude" #>
<# 
 var config = new ConfigurationAccessor((IServiceProvider)this.Host,  @"path\to\ProjectWithConfig.csproj");
 string connectionString =   config.ConnectionStrings["MainConnectionString"].ConnectionString;
#>
Comments