Robert's blog
Robert Važan

Entity Framework T4 view generator and .NET 4.5

Pre-generated Entity Framework views significantly speed up application startup. They are fairly easy to do with the built-in T4 template engine.

I have been happily using Sanjay Nagamangalam's T4 template to pre-generate Entity Framework views, which substantially speeds up application startup. Unfortunately after upgrading the project to .NET 4.5, the T4 view generator failed with the following error:

Running transformation: System.ArgumentException: Argument 'xmlReaders' is not valid.  The set contains a null value.
   at System.Data.EntityUtil.CheckArgumentContainsNull[T](IEnumerable`1& enumerableArgument, String argumentName)
   at System.Data.Metadata.Edm.EdmItemCollection..ctor(IEnumerable`1 xmlReaders)
   at Microsoft.VisualStudio.TextTemplatingAED4E591A16DD045E483CD58B20DB126.GeneratedTextTransformation.GenerateViews(String edmxFilePath)

It turns out the original T4 template is incompatible with .NET 4.5. I've fixed it to support .NET 4.5. See below for the fixed code.

The fix consists of two changes to the original code:

The second point means that the view generator now requires .NET Framework 4 or higher. That's not a big deal since if you are reading this, you are likely using .NET 4.5 already.

<#
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.

THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/
#>

<#
    //
    // TITLE: T4 template to generate views for an EDMX file in a C# project
    //
    // DESCRIPTION:
    // This is a T4 template to generate views in C# for an EDMX file in C# projects.
    // The generated views are automatically compiled into the project's output assembly.
    //
    // This template follows a simple file naming convention to determine the EDMX file to process:
    // - It assumes that [edmx-file-name].Views.tt will process and generate views for [edmx-file-name].EDMX
    // - The views are generated in the code behind file [edmx-file-name].Views.cs
    //
    // USAGE:
    // Do the following to generate views for an EDMX file (e.g. Model1.edmx) in a C# project
    // 1. In Solution Explorer, right-click the project node and choose "Add...Existing...Item" from the context menu
    // 2. Browse to and choose this .tt file to include it in the project 
    // 3. Ensure this .tt file is in the same directory as the EDMX file to process 
    // 4. In Solution Explorer, rename this .tt file to the form [edmx-file-name].Views.tt (e.g. Model1.Views.tt)
    // 5. In Solution Explorer, right-click Model1.Views.tt and choose "Run Custom Tool" to generate the views
    // 6. The views are generated in the code behind file Model1.Views.cs
    //
    // TIPS:
    // If you have multiple EDMX files in your project then make as many copies of this .tt file and rename appropriately
    // to pair each with each EDMX file.
    //
    // To generate views for all EDMX files in the solution, click the "Transform All Templates" button in the Solution Explorer toolbar
    // (its the rightmost button in the toolbar) 
    //
#>

<#
    //
    // T4 template code follows
    //
#>

<#@ template language="C#" hostspecific="true"#>
<#@ output extension=".cs" #>

<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="System.Data.Entity.Design" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>

<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.Entity.Design" #>
<#@ import namespace="System.Data.Metadata.Edm" #>
<#@ import namespace="System.Data.Mapping" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>

<# 
    // Find EDMX file to process: Model1.Views.tt generates views for Model1.EDMX
    string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".views", "") + ".edmx";
    string edmxFilePath = Path.Combine(Path.GetDirectoryName(this.Host.TemplateFile), edmxFileName);
    if (File.Exists(edmxFilePath))
    {
        // Call helper class to generate pre-compiled views and write to output
        this.WriteLine(GenerateViews(edmxFilePath));
    }
    else
    {
        this.Error(String.Format("No views were generated. Cannot find file {0}. Ensure the project has an EDMX file"
        	+ " and the file name of the .tt file is of the form [edmx-file-name].Views.tt", edmxFilePath));
    }
    
    // All done!
#>

<#+
    private String GenerateViews(string edmxFilePath)
    {
        String generatedViews = String.Empty;
        try
        {
            using (StreamWriter writer = new StreamWriter(new MemoryStream()))
            {
                XmlReader csdlReader = null;
                XmlReader mslReader = null;
                XmlReader ssdlReader = null;
                Version version;

                // Crack open the EDMX file and get readers over the CSDL, MSL and SSDL portions
                GetConceptualMappingAndStorageReaders(edmxFilePath, out csdlReader, out mslReader, out ssdlReader, out version);

                // Initialize item collections
                EdmItemCollection edmItems = new EdmItemCollection(new XmlReader[] { csdlReader });
                StoreItemCollection storeItems = new StoreItemCollection(new XmlReader[] { ssdlReader });
                StorageMappingItemCollection mappingItems = new StorageMappingItemCollection(edmItems, storeItems, new XmlReader[] { mslReader });

                // Initialize the view generator to generate views in C#
                EntityViewGenerator viewGenerator = new EntityViewGenerator();
                viewGenerator.LanguageOption = LanguageOption.GenerateCSharpCode;
                IList<EdmSchemaError> errors = viewGenerator.GenerateViews(mappingItems, writer, version);

                foreach (EdmSchemaError e in errors)
                {
                    // log error
                    this.Error(e.Message);
                }

                MemoryStream memStream = writer.BaseStream as MemoryStream;
                generatedViews = Encoding.UTF8.GetString(memStream.ToArray());
            }
        }
        catch (Exception ex)
        {
            // log error
            this.Error(ex.ToString());
        }

        return generatedViews;
    }

    private void GetConceptualMappingAndStorageReaders(
    	string edmxFile, out XmlReader csdlReader, out XmlReader mslReader, out XmlReader ssdlReader, out Version version)
    {
        csdlReader = null;
        mslReader = null;
        ssdlReader = null;
        version = EntityFrameworkVersions.Version1;

        XNamespace edmxns;
        XNamespace csdlns;
        XNamespace mslns;
        XNamespace ssdlns;

        XNamespace edmxns_V1 = "http://schemas.microsoft.com/ado/2007/06/edmx";
        XNamespace edmxns_V2 = "http://schemas.microsoft.com/ado/2008/10/edmx";
        XNamespace edmxns_V3 = "http://schemas.microsoft.com/ado/2009/11/edmx";
        
        XDocument edmxDoc = XDocument.Load(edmxFile);
        if (edmxDoc != null)
        {
            // try to parse the Edmx file using V1 namespace
            XElement edmxNode = edmxDoc.Element(edmxns_V1 + "Edmx");
            if (edmxNode == null)
            {
                edmxNode = edmxDoc.Element(edmxns_V2 + "Edmx");
                if (edmxNode == null)
                {
                    // try to parse the Edmx file using V3 namespace
                    edmxNode = edmxDoc.Element(edmxns_V3 + "Edmx");
                    edmxns = "http://schemas.microsoft.com/ado/2009/11/edmx";
                    csdlns = "http://schemas.microsoft.com/ado/2009/11/edm";
                    mslns = "http://schemas.microsoft.com/ado/2009/11/mapping/cs";
                    ssdlns = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
                    version = EntityFrameworkVersions.Version3;
                }
                else
                {
                    // the Edmx file is in V2 namespace
                    edmxns = "http://schemas.microsoft.com/ado/2008/10/edmx";
                    csdlns = "http://schemas.microsoft.com/ado/2008/09/edm";
                    mslns = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
                    ssdlns = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl";
                    version = EntityFrameworkVersions.Version2;
                }
            }
            else
            {
                // the Edmx file is in V1 namespace
                edmxns = "http://schemas.microsoft.com/ado/2007/06/edmx";
                csdlns = "http://schemas.microsoft.com/ado/2006/04/edm";
                mslns = "urn:schemas-microsoft-com:windows:storage:mapping:CS";
                ssdlns = "http://schemas.microsoft.com/ado/2006/04/edm/ssdl";
            }

            if (edmxNode != null)
            {
                
                XElement runtimeNode = edmxNode.Element(edmxns + "Runtime");
                if (runtimeNode != null)
                {
                    // Create XmlReader over CSDL in EDMX
                    XElement conceptualModelsNode = runtimeNode.Element(edmxns + "ConceptualModels");
                    if (conceptualModelsNode != null)
                    {
                        XElement csdlContent = conceptualModelsNode.Element(csdlns + "Schema");
                        if (csdlContent != null)
                        {
                            csdlReader = csdlContent.CreateReader();
                        }
                    }

                    // Create XmlReader over MSL in EDMX
                    XElement mappingsNode = runtimeNode.Element(edmxns + "Mappings");
                    if (mappingsNode != null)
                    {
                        XElement mslContent = mappingsNode.Element(mslns + "Mapping");
                        if (mslContent != null)
                        {
                            mslReader = mslContent.CreateReader();
                        }
                    }

                    // Create XmlReader over SSDL in EDMX
                    XElement storageModelsNode = runtimeNode.Element(edmxns + "StorageModels");
                    if (storageModelsNode != null)
                    {
                        XElement ssdlContent = storageModelsNode.Element(ssdlns + "Schema");
                        if (ssdlContent != null)
                        {
                            ssdlReader = ssdlContent.CreateReader();
                        }
                    }
                }
            }
        }
    }
#>

Enjoy!

Comments

Thanks a lot, dude! Really, I mean it! :)
Anonymous
Great. Thanks. You can now download this T4 from codeplex.
https://ef6viewgenerator.codeplex.com/downloads/get/846148
Thang Believe
Oops, broken URL?
Robert Važan
Any new about the broken URL?
Where Can I download it?

I tried to copy&paste the source code but I receive the followig error:

Error 1 Running transformation: System.Data.ProviderIncompatibleException: The provider did not return a ProviderManifest instance. ---> System.ArgumentException: Could not determine storage version; a valid storage connection or a version hint is required.
at System.Data.SqlClient.SqlVersionUtils.GetSqlVersion(String versionHint)
at System.Data.SqlClient.SqlProviderManifest..ctor(String manifestToken)
at System.Data.SqlClient.SqlProviderServices.GetDbProviderManifest(String versionHint)
at System.Data.Common.DbProviderServices.GetProviderManifest(String manifestToken)
--- End of inner exception stack trace ---
at System.Data.Common.DbProviderServices.GetProviderManifest(String manifestToken)
at System.Data.Metadata.Edm.StoreItemCollection.Loader.InitializeProviderManifest(Action`3 addError)
at System.Data.Metadata.Edm.StoreItemCollection.Loader.OnProviderManifestTokenNotification(String token, Action`3 addError)
at System.Data.EntityModel.SchemaObjectModel.Schema.HandleProviderManifestTokenAttribute(XmlReader reader)
at System.Data.EntityModel.SchemaObjectModel.Schema.HandleAttribute(XmlReader reader)
at System.Data.EntityModel.SchemaObjectModel.SchemaElement.ParseAttribute(XmlReader reader)
at System.Data.EntityModel.SchemaObjectModel.SchemaElement.Parse(XmlReader reader)
at System.Data.EntityModel.SchemaObjectModel.Schema.HandleTopLevelSchemaElement(XmlReader reader)
at System.Data.EntityModel.SchemaObjectModel.Schema.InternalParse(XmlReader sourceReader, String sourceLocation)
at System.Data.EntityModel.SchemaObjectModel.Schema.Parse(XmlReader sourceReader, String sourceLocation)
at System.Data.EntityModel.SchemaObjectModel.SchemaManager.ParseAndValidate(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModel, AttributeValueNotification providerNotification, AttributeValueNotification providerManifestTokenNotification, ProviderManifestNeeded providerManifestNeeded, IList`1& schemaCollection)
at System.Data.Metadata.Edm.StoreItemCollection.Loader.LoadItems(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths)
at System.Data.Metadata.Edm.StoreItemCollection.Init(IEnumerable`1 xmlReaders, IEnumerable`1 filePaths, Boolean throwOnError, DbProviderManifest& providerManifest, DbProviderFactory& providerFactory, String& providerManifestToken, Memoizer`2& cachedCTypeFunction)
at System.Data.Metadata.Edm.StoreItemCollection..ctor(IEnumerable`1 xmlReaders)
at Microsoft.VisualStudio.TextTemplating10967D970D6A6A2EC928E833F47BE3597044F27CEA90F392B1539963361BE1CA8FA03991BA5ED77C354E905AF9BD5DDE3369638DCEE327644C288C681D9168E4.GeneratedTextTransformation.GenerateViews(String edmxFilePath) 1 1

any help?

Thanks
Daniele
Daniele Barbini
Ok the link to use shold be this:
http://ef5viewgenerator.codeplex.com/

at least for .net 4.5.

Bye
Daniele Barbini
See mitaka's post, solved for me.

http://stackoverflow.com/questions/19574981/entity-framework-the-provider-did-not-return-a-providermanifest-instance
Anonymous
Comments are closed for this post.