EF6 CodeFirst View Generation T4 Template for C# updated for EF6 Beta1

Entity Framework 6 Beta 1 has shipped. The good news is that among (many) others it contains a few changes to view generation. There is now a public API on the StorageMappingItemCollection type that can return views for a given StorageMappingItemCollection instance. The bad news is that some of the changes we introduced in this area in the Beta version are breaking. This means that views created with the EF6 CodeFirst View Generation T4 Template for C# I created for the EF6 Alpha will no longer work with the Beta version and need to be recreated. The main changes (apart from adding the public API) to view generation in Beta are:

Because of the above breaking changes I updated the EF6 CodeFirst View Generation T4 Template for C# to work with EF6 Beta 1. Note that the new version uses the new public API for view generation and therefore it requires that your project has a reference to the EntityFramework.dll shipped with EF6 Beta 1. Also, you cannot generate views with the new version and re-use them in an EF6 Alpha project.
Installation steps have not changed comparing to the previous version and you can found them in this post. You shall not have to uninstall the previous version – the new vsix should replace the old one.

32 thoughts on “EF6 CodeFirst View Generation T4 Template for C# updated for EF6 Beta1

  1. I installed the EF 6.0.0-beta1 package fron Nuget, and the GenerateViews method is nowhere to be found. Of course, the TT template does not run because it cannot find that method. Are you sure this API change is in the beta1 build on Nuget?

    Like

    1. @Samuele Ruggieri – I was able to reproduce what you are seeing. I tested the template on one of the nigtly builds and it worked. I will follow up on this and will let you know. Thanks for reporting.

      Like

    2. OK. I updated the template in the VS Gallery. It will now work for the Beta release but not for nightly builds. It may not work if the Beta version of tooling is installed. I will look into this issue later this week.

      Like

      1. I installed 0.2.1, uninstalled the beta EF Tools and the template ran succesfully. I’m getting a MappingException though (“The mapping and metadata information for EntityContainer ‘AutodromiContext’ no longer matches the information used to create the pre-generated views.”), which disappears as soon as I delete the pre-generated views. Thanks for following up by the way.

        Like

        1. You need to re-create views if your model changes (right click the T4 template and select “Run Custom Tool”). If this does not help – can you provide a repro? I would like to investigate this as this might be a bug in EF6.

          Like

  2. I got the exact same error as Samuele’s. Everything works fine as soon as I delete the pre-generated views

    System.Data.Entity.Core.EntityCommandCompilationException : An error occurred while preparing the command definition. See the inner exception for details.
    —-> System.Data.Entity.Core.MappingException : The mapping and metadata information for EntityContainer ‘CwDbContext’ no longer matches the information used to create the pre-generated views.
    at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory)
    at System.Data.Entity.Core.EntityClient.Internal.EntityProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree)
    at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.CreateCommandDefinition(ObjectContext context, DbQueryCommandTree tree)
    at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlanFactory.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Boolean streaming, Span span, IEnumerable`1 compiledQueryParameters, AliasGenerator aliasGenerator)
    at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
    at System.Data.Entity.Core.Objects.ObjectQuery`1.c__DisplayClassb.b__a()
    at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction(Func`1 func, Boolean throwOnExistingTransaction, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
    at System.Data.Entity.Core.Objects.ObjectQuery`1.c__DisplayClassb.b__9()
    at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Func`1 func)
    at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
    at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable.GetEnumerator>b__0()
    at System.Lazy`1.CreateValue()
    at System.Lazy`1.LazyInitValue()
    at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
    at System.Linq.Enumerable.Single(IEnumerable`1 source)
    at System.Linq.Queryable.Any(IQueryable`1 source)
    at Cityworks.DataAccess.Seeder.SeedAuditTraces() in Seeder.cs: line 19
    at Cityworks.DataAccess.Seeder.SeedAll() in Seeder.cs: line 11637
    at Cityworks.DataAccess.Configuration.Seed(CwDbContext context) in Configuration.cs: line 32
    at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
    at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
    at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
    at Cityworks.DataAccess.Tests.DbMigrationTests.Migrator_TargetMigrationCurrent_ReturnsTrue() in DbMigrationTests.cs: line 54
    –MappingException
    at System.Data.Entity.Core.Mapping.StorageMappingItemCollection.ViewDictionary.SerializedAddGeneratedViewsInEntityViewContainer(MetadataWorkspace workspace, EntityViewContainer entityViewContainer, Dictionary`2 extentMappingViews)
    at System.Data.Entity.Core.Mapping.StorageMappingItemCollection.ViewDictionary.SerializedCollectViewsFromCache(MetadataWorkspace workspace, Dictionary`2 extentMappingViews, Func`1 getEntryAssembly)
    at System.Data.Entity.Core.Mapping.StorageMappingItemCollection.ViewDictionary.SerializedGetGeneratedViews(EntityContainer container)
    at System.Data.Entity.Core.Common.Utils.Memoizer`2.Result.GetValue()
    at System.Data.Entity.Core.Common.Utils.Memoizer`2.Evaluate(TArg arg)
    at System.Data.Entity.Core.Mapping.StorageMappingItemCollection.ViewDictionary.GetGeneratedView(EntitySetBase extent, MetadataWorkspace workspace, StorageMappingItemCollection storageMappingItemCollection)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.ExpandView(ScanTableOp scanTableOp, ref IsOfOp typeFilter)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.ProcessScanTable(Node scanTableNode, ScanTableOp scanTableOp, ref IsOfOp typeFilter)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Visit(ScanTableOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.VisitScalarOpDefault(ScalarOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Visit(ExistsOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.VisitScalarOpDefault(ScalarOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Visit(ConditionalOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.VisitScalarOpDefault(ScalarOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Visit(ConditionalOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.VisitScalarOpDefault(ScalarOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Visit(CaseOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfNode.VisitDefault(Node n)
    at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfNode.VisitAncillaryOpDefault(AncillaryOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfNode.VisitDefault(Node n)
    at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfNode.VisitAncillaryOpDefault(AncillaryOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitRelOpDefault(RelOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Visit(ProjectOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.SubqueryTrackingVisitor.VisitChildren(Node n)
    at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfNode.VisitDefault(Node n)
    at System.Data.Entity.Core.Query.InternalTrees.BasicOpVisitorOfNode.VisitPhysicalOpDefault(PhysicalOp op, Node n)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Process(ref Dictionary`2 tvfResultKeys)
    at System.Data.Entity.Core.Query.PlanCompiler.PreProcessor.Process(PlanCompiler planCompilerState, ref StructuredTypeInfo typeInfo, ref Dictionary`2 tvfResultKeys)
    at System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Compile(ref List`1 providerCommands, ref ColumnMap resultColumnMap, ref Int32 columnCount, ref Set`1 entitySets)
    at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition..ctor(DbProviderFactory storeProviderFactory, DbCommandTree commandTree, IDbDependencyResolver resolver, BridgeDataReaderFactory bridgeDataReaderFactory, ColumnMapFactory columnMapFactory)

    Like

  3. Migrations work great! The problem is on Running Seed method. Can you send me a simple project that works in your environment? I can try to see if it works in mine.
    thanks

    Like

        1. The issue is fixed indeed but the API around view generation has been changing a lot. We have not settled on this yet so I am waiting with updating the template. I also typically don’t update the template for nightly builds since, due to changes we are making oftentimes sometimes that would work with a nightly would not work with the “official”/non-nightly release. To make it work I would probably have to have two separate templates – one for nightlies and one for the releases. Anyways, I am going to update the template when we ship the next version (hopefully we will be done with API changes and the template will work for “official” and nightly builds).

          Thanks,
          Pawel

          Like

      1. Thanks for the fast response… sorry for the duplicate post, a strange thing happened when I logged in and it seemed the first reply hadn’t gone through.

        Makes sense what you are saying .. I understand you not wanting to create more work, that you’ll then have to change later.

        So, just to be clear – at the moment there is no way to generate the view for EF6, until you fix the template? The issue with the mis-matching hashes, there is no work around for?

        Best Regards,

        Will

        Like

        1. This is correct for EF6 Beta1. On the other hand I would like to undrestand what is the reason why you need views? Are you seeing any performance issues you hope pre-generated views would fix? How big is your model? How long does it take to run the first query (this is when the views are generated internally)? I am asking since there have been some significant improvements in the view gen area in EF6 which should reduce the need to using pre-generated views.

          Thanks,
          Pawel

          Like

      2. Hi Pawel,

        Thanks for the follow-up .. to be honest I’m just looking at all / anything that can improve performance further in the application, so using pre-generated views seemed like another area that could potentially speed things up a fraction.

        The model is not that large.. around 100 entities, so perhaps pre-generated views would not help much anyway?

        We’ve had quite a few issues with performance in the application we are building, too much to go into here. The DBA and myself come from a stored procedures dev. approach background and in transitioning to EF5-6 we’ve seen a substantial drop in performance, as we’d compare to an application built using our previous approach / methodologies.

        I’ve taken a multi-tier approach to tackling the issue and improving performance, which is good now overall… however its still not blazing as it should be really. We are working with LinQ > SQL structures initially and then moving into LinQ > Object manipulation thereafter, when sculpting the data into the final forms we are using .. performance just doesn’t seem quite right.. even still.

        Anyways… thanks for your time, good job on the template code; I’ve bookmarked your site, so will follow with interest for future reference.

        Best Regards,

        Will

        Like

        1. Hi Will,

          Thanks a lot for the comprehensive answer!

          For a model with ~100 entities view generation should not be a bottleneck – especially in EF6. As a side note – even though the template is broken for the beta you could use it to measure how much start up time pre-generated times could potentially saved you – the code we need to run to generate view is the same regardless whether we do it at design time (e.g. T4 template) or at runtime. So, you can measure how long it takes to generate views with the T4 template (obviously there will be some overhead when using the template related to spinning a separate app domain (this is what the templating engine does) and then there is some I/O related to writing the ouput to files, etc.) and this is, roughly, how much you would have saved at start-up time.
          In general you (or your DBA) should not expect any ORM to perform as fast as raw SQL queries. However the toll you pay should not be prohibitive either. For EF6, even though we have added a ton of new features, our baseline is EF5 and we try hard not to be slower than we were in EF5.
          With regards to stored procedures – using stored procedures may sometimes help indeed. In EF6 we introduced mapping CUD (Create-Update-Delete) operations to stored procedures when using Code First approach (mapping to stored procedures for Model First / Database First approaches have been supported since the version 1). On the other hand I don’t think that CUD operations really kill performance – I think they are more useful for controlling access rather than to improve performance. I think a bigger problem might be with queries EF generates – query pipeline has to be general enough to be able to create queries for supported Linq expressions that can be then translated to a specific SQL dialect by the provider. To simplify (or generalize) queries EF introduces constructs (e.g. subqueries, joins) that are semantically correct but redundant when you look at the given query. This sometimes may cause obscure problems when a construct is a no-op for one database/provider but is actually pretty costly (or even not supported – vide CROSS APPLY) for a different provider.
          In any case, for performance problems – if you have not already – I would start from the EF performance consideration (http://msdn.microsoft.com/en-us/library/cc853327.aspx). If this does not resolve your problem try isolating it – it should be relatively easy for EF6 since both symbols and source are available. Once you isolate the problem you can start a new thread or file a bug on the EF codeplex site (http://entityframework.codeplex.com). We are focusing on closing the EF6 release so it is unlikely an issue – if found – will be fixed in this release, but it is possible that someone will provide at least a workaround to your problem.

          Hope this helps,
          Pawel

          Like

      3. Hi Pawel,

        Thanks for the additional information and pointers on the performance considerations, as relates to view generation … very useful; it really gives me a decent context to better understand how pre-generating the views impacts the cold start-up performance. Makes a lot of sense what you are saying to be honest and I had perceived an improvement in the start-up performance with EF6.

        The other other information you’ve provided is very helpful also, particularly the link on Performance Considerations. Also, as you mentioned the SQL generated by EF can be a key area for performance .. we noticed this also in testing; it wasn’t until we profiled all the queries and put additional indexes on the database, that performance dramatically improved.

        I’ll dig further into the information you’ve provided next week and see where it leads… I’m hopeful that we’ll find something else that can be improved, armed with this new knowledge!

        Thanks again!

        Best Regards,

        Will

        Like

  4. I created a small project using code first, a single poco class with ID (pk) and Name properties only.
    When I add the T4 I get the error:

    System.InvalidCastException: [A]System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection cannot be cast to [B]System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection. Type A originates from ‘EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ in the context ‘Default’ at location ‘C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\EntityFramework.dll’. Type B originates from ‘EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ in the context ‘LoadFrom’ at location ‘C:\Apagar\WpfApplication1\packages\EntityFramework.6.0.0-beta1\lib\net45\EntityFramework.dll’.

    Like

    1. Looks like if you have the new EF Designer installed the T4 template is using the EntityFramework.dll installed with the tooling instead of the one you have in your project. I need to investigate how to prevent this from happening. Thanks for reporting.

      Like

  5. I used the ‘DbConfiguration’ configuration approach to solve the previous problem, but now with the rc1, the ‘mappingItemCollection.Generate’ does not exist and I can’t use the .tt to generate the views. Any leads on how to solve this?

    Like

    1. It was actually a naming problem only, some changes are required to fix the file to work with the new version, but nothing big. The example I used in my comment can be fixed By changing to ‘mappingItemCollection.GenerateViews’ and little changes in the code that consumes it to create the views.

      Like

      1. Glad that you were able to solve the problem – can you share how you used the DbConfiguration to fix this? I started looking at this and found that the InvalidCastException is a result of having two EntityFramework.dll loaded in one AppDomain due to the rules used by Fusion to bind types mixed with how T4 template engine creates an app domain for the transformation. The view gen API changed RC1 (which shipped today). I updated the template accordingly and if you are interested you can find it here: https://github.com/moozzyk/EF6CodeFirstViewGenT4TemplateCSharp. The problem is that the RC1 (similarly to Beta) has some bugs in this area that prevent from using pregenerated views. On top of that the T4 template does not work if you have EF6 tooling installed due to the InvalidCastException you posted about above. The issues with the EF6 should already be fixed in nightly builds. I am going to fix the template to work correctly even if new EF tooling is installed soon – just follow my github repo.

        Like

      2. The DBConfiguration class goes like this:

        Public Class SqlConfiguration
        Inherits DbConfiguration

        Public Sub New()
        SetDefaultConnectionFactory(New LocalDbConnectionFactory(“v11.0”))
        SetProviderServices(“System.Data.Entity.SqlServer.SqlProvider”, System.Data.Entity.SqlServer.SqlProviderServices.Instance)
        End Sub

        End Class

        I removed the EF section from my config file and added this class to the project.
        Since it happens bacause of a bug I think I will remove this after the bug is fixed.

        Thank you for the update on the .tt! And about the bug on the RC1, it simply ignores the pre-generated views?

        Like

        1. I spent some time looking at the root cause of the InvalidCastException and it seems unlikely that going from config file based configuration to code based configuration would fix the issue but I might be wrong. In general the template will have to be fixed so that it can handle web/app.config anyways because some people will always have this. Unfortunately the “official” RC1 (i.e. the one that shipped yesterday as opposed to the RC1 from nightly builds) has a few bugs related to generated views with one that is blocking: https://entityframework.codeplex.com/workitem/1531 – it asks for the views that don’t actually exist and it does not accept null as the response. The fix did not fit to the official RC1 release but is already in the nightly builds.

          Like

      3. You are right. It was not the change to code based configuration that fixed it, it was the addition of a reference to EntityFramework.SqlServer in the T4. I removed the code based configuration and it worked normally, but if I remove the reference from the T4 I get the error again.
        As you said I’m using the nightly builds (even if I have to fix the T4 from time to time). With some changes, the views are being generated, saved on the xml and the ViewsForBaseEntitySets class is being used to load and look for the views.

        But now I’m having 1 new problem… At debug time, in Visual Studio, the first query is not calling the DbMappingViewCache.GetView method. The method is being called, but after the EDMX was generated and loaded into memory. In runtime it seems to work, since it loads very fast (debug takes 2 minutes, running it takes 20 seconds).

        One other thing is that the EdmxWriter.WriteEdmx is generating 2 of each view. A “CodeFirstDataBase.ViewName” and one “MyDBContextName.ViewName” for each view, I disabled migrations and code first database creation by setting the initializer to Nothing (with Database.SetInitializer). Since its a very big database having 2 of each makes the file kind of big.

        Thank you for your continuous help =D

        Like

        1. Hi Adauto,

          A couple weeks ago I updated the T4 template for EF6 to fix the issue with the InvalidCastException. It is not available on VS Gallery yet but you can get it from my github.
          I am not sure why views would only be used when running tha app as opposed to when debugging it. I have not seen this happen before. EF does not make any assumptions whether it is running under debugger or not (or in general whether the code is Debug vs. Release). Are you sure you are running the same app in both cases?
          EF creates one query view and one update view for each entity set. If I remember correctly it will also create an update view for each independent association set. There is no way to remove any of the views. If you do EF will throw.

          Hope this helps,
          Pawel

          Like

      4. I am sure its the same project, but if its normal to generate 2 views for each EntitySet of the DBContext I guess I’ll have to split the file in two, using the first part of the view name (the ” CodeFirstDataBase” and ” MyDBContextName”).
        I guess the fact that its slow on debug could be some configuration in my Visual Studio or something, I’m using the RC from 2013, so I guess I’ll wait till it releases to look into the debug/run time problem.

        But while doing some tests I found out that when I run the first query, EF generates the views as if I don’t had the ‘DBMappingViewCacheType’ but after the views are generated it call the ‘GetView’ that looks for the view in the XML file… but it already took time to generate. Am I missing something? Its supposed to take a generate the views and then look into the xml? It is supposed to call ‘GetView’ right from the start?

        Like

        1. If you look carefully it is actually just one view for each entity set. The deal is that there are 2 entity sets that are associated with one DbSet. One is from the CSpace (a.k.a. conceptual model) and the other is from the SSpace (a.k.a. store model). Since you are using CodeFirst you don’t really see the store model and the conceptual model is also built for you from your classes. If you, however, looked at the edmx (for code first you can use EdmxWriter to get the edmx corresponding for your model) you would see both CSpace and SSpace and how they are mapped to each other. There you would see both entity sets and how and the mapping between the two.
          Regarding the “double” view generation – it’s interesting you noticed that. I believe what you are seeing is view generation for HistoryContext. As you probably know EF Migrations keeps the track of applied migrations in the database. To access this database EF is internally using… EF. HistoryContext is a DbContext derived class used internally to read data from the MigrationHistoryTable. I believe the view generation you are seeing is for this context.

          Like

      5. But I use

        Public Class MyContextConfiguration
        Inherits DbConfiguration

        Public Sub New()
        SetDatabaseInitializer(Of MyContext)(Nothing)

        and removed the table _MigrationHistory from my database. Did I miss something?

        Like

      6. Also did this… just in case… no change.

        Public NotInheritable Class MyContextMigrationsConfiguration
        Inherits DbMigrationsConfiguration(Of MyContext)

        Public Sub New()
        AutomaticMigrationsEnabled = False
        AutomaticMigrationDataLossAllowed = False
        End Sub

        End Class

        Like

Leave a comment