Entity Framework Code First and Pre-generated Views

When working with Entity Framework view generation may take a long time for bigger or complicated models. The workaround for this problem is to use pre-generated views. In case of Database First and Model First approaches you can use T4 templates that will create the pre-generated views (you can find more details here). But what to do when using Code First approach? One possibility is to use Entity Framework Power Tools – just right-click on the file containing your DbContext derived class and select “Optimize Data Model”. Voila – views are created. But what if you need more control, cannot use UI (e.g. you want your build system to create the pre-generated views) or the tool for whatever reason does not work? You can for instance follow this 5 steps:

  • Get Edmx file for your model using EdmxWriter.WriteEdmx() method
  • Retrieve csdl, msl, ssdl from the edmx file and save them
  • From the Visual Studio Command Prompt run EdmGen with mode parameter set to ViewGeneration (i.e. /mode:ViewGeneration)
  • Add the generated C#/VB.Net file to your project
  • Repeat each time you change your model

Easy? Maybe. Error prone? Probably. Cumbersome? For sure. But now there is a third way. Similarly to the T4 templates for Model and Database First workflows I created T4 templates for creating pre-generated views for Code First approach. Now, you would just add the right (C# or VB.Net) template to your project, rename it so that it contains the name of the context you want to create the views for (e.g. MyContext.Views.tt) and generate your views by right clicking the template and selecting “Run Custom Tool” menu option. Note that creating the pre-generated views with the template will take approximately the same amount of time it takes to create views at runtime – you are just doing the work that would be done at runtime at design time. The templates are (temporarily) available here. I will update this post when they are moved to the final location.The templates are available on the Visual Studio Code Gallery. See this post for more details.

Pawel Kadluczka

Advertisements

32 thoughts on “Entity Framework Code First and Pre-generated Views

  1. Matt says:

    Hi,

    I wanted to try this out but I ran into a problem.

    If my DbContext constructor looks like this:

    public AppContext()
    : base(“name=ApplicationDatabase”)
    {
    }

    Then I get the following error:

    Error 15 Running transformation: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.InvalidOperationException: No connection string named ‘ApplicationDatabase’ could be found in the application config file.
    at System.Data.Entity.Internal.LazyInternalConnection.get_ConnectionHasModel()
    at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
    at System.Data.Entity.Internal.InternalContext.ForceOSpaceLoadingForKnownEntityTypes()
    at System.Data.Entity.DbContext.System.Data.Entity.Infrastructure.IObjectContextAdapter.get_ObjectContext()

    — End of inner exception stack trace —
    at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
    at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
    at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
    at System.Activator.CreateInstance(Type type, Boolean nonPublic)
    at Microsoft.VisualStudio.TextTemplating1126FAA999F92E9E0448C54D7533B7BC.GeneratedTextTransformation.GetEdmx(Type contextType)
    at Microsoft.VisualStudio.TextTemplating1126FAA999F92E9E0448C54D7533B7BC.GeneratedTextTransformation.GenerateViews(String contextTypeName)
    at Microsoft.VisualStudio.TextTemplating1126FAA999F92E9E0448C54D7533B7BC.GeneratedTextTransformation.TransformText()
    at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result)

    If I change my constructor to:

    public AppContext()
    : base(“ApplicationDatabase”)
    {
    this.ObjectContext.SavingChanges += new EventHandler(OnSavingChanges);
    }

    Then I get a different error:

    Error 15 Running transformation: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.Data.DataException: An exception occurred while initializing the database. See the InnerException for details. —> System.Data.EntityException: The underlying provider failed on Open. —> System.Data.SqlClient.SqlException: Cannot open database “XXXXXXXXXXXXXX” requested by the login. The login failed.
    Login failed for user ‘#######\#######’.
    at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
    at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
    at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
    at System.Data.SqlClient.SqlConnection.Open()
    at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
    — End of inner exception stack trace —
    at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
    at System.Data.EntityClient.EntityConnection.Open()
    at System.Data.Objects.ObjectContext.EnsureConnection()
    at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
    at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator()
    at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
    at System.Data.Objects.ELinq.ObjectQueryProvider.b__1[TResult](IEnumerable`1 sequence)
    at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
    at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression)
    at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression)
    at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
    at System.Data.Entity.Internal.EdmMetadataRepository.QueryForModelHash(Func`2 createContext)
    at System.Data.Entity.Internal.InternalContext.QueryForModelHash()
    at System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel(InternalContext internalContext, ModelHashCalculator modelHashCalculator, Boolean throwIfNoMetadata)
    at System.Data.Entity.Internal.InternalContext.CompatibleWithModel(Boolean throwIfNoMetadata)
    at System.Data.Entity.CreateDatabaseIfNotExists`1.InitializeDatabase(TContext context)
    at System.Data.Entity.Internal.InternalContext.c__DisplayClass8.b__6()
    at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
    — End of inner exception stack trace —
    at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
    at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
    at System.Data.Entity.Internal.LazyInternalContext.b__4(InternalContext c)
    at System.Data.Entity.Internal.RetryAction`1.PerformAction(TInput input)
    at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action`1 action)
    at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
    at System.Data.Entity.Internal.InternalContext.ForceOSpaceLoadingForKnownEntityTypes()
    at System.Data.Entity.DbContext.System.Data.Entity.Infrastructure.IObjectContextAdapter.get_ObjectContext()

    — End of inner exception stack trace —
    at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
    at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
    at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
    at System.Activator.CreateInstance(Type type, Boolean nonPublic)
    at Microsoft.VisualStudio.TextTemplating1126FAA999F92E9E0448C54D7533B7BC.GeneratedTextTransformation.GetEdmx(Type contextType)
    at Microsoft.VisualStudio.TextTemplating1126FAA999F92E9E0448C54D7533B7BC.GeneratedTextTransformation.GenerateViews(String contextTypeName)
    at Microsoft.VisualStudio.TextTemplating1126FAA999F92E9E0448C54D7533B7BC.GeneratedTextTransformation.TransformText()
    at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result)

    Are you able to help?

    Liked by 1 person

    • moozzyk says:

      Thanks for reporting this. The reason for the first exception is that the config settings from the project is not loaded to the AppDomain the transformation runs. I will look into this shortly. I am not sure at the moment why there is a different exception in the second case.

      Like

      • Matt says:

        No problem. A couple of things to note:

        Looking back over my previous comment, I included an event handler in the second constructor example. That isn’t relevant. The significant difference is the connection string parameter passed to the base constructor – “connstring” and “name=connstring”. 

        I actually got your template to work by including a call to “Database.SetInitializer(null)”. 

        I hope this information is useful!

        Like

        • moozzyk says:

          It would be interesting to see the method that calls ((IObjectContextAdapter)ctx).ObjectContext (this is line 128 in your code – at Core.Model.AppContext.get_ObjectContext() …). It’s causing the database initialization. I checked and EdmxWriter.WriteEdmx() by itself should not initialize the database. Anyways the scenario with the app.config file was broken. I have a fix for this and will post it shortly

          Like

      • Matt says:

        Hi,

        Please may I request that you kindly remove the stack traces from my comments? I didn’t think when I posted but some of the information is potentially sensitive – my fault entirely.

        We can exchange more info via email if you like?

        Thanks

        Like

  2. […] time ago I created T4 templates for creating pre-generated views for Entity Framework Code First applications. I wanted to make them available as soon as possible so I just uploaded them as a zip file to one […]

    Like

  3. Doug R says:

    Hi

    I am running the latest update of EF 5 Pre and Visual Studio 2012RC. I have been having issues with the controler and view generation so I tried loading this in the hope it would fix some issues.

    When loading this package I get the following error;

    Error 5
    Compiling transformation: The type ‘System.Data.Entity.DbContext’ exists in both ‘c:\Users\drobertson123\Documents\Visual Studio 11\Projects\proedgetradinggroup\proedgetradinggroup\bin\EntityFramework.dll’ and ‘c:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Data.Entity\v4.0_4.2.0.0__b77a5c561934e089\System.Data.Entity.dll’ c:\Users\drobertson123\Documents\Visual Studio 11\Projects\proedgetradinggroup\proedgetradinggroup\CodeFirstCSharp.Views1.tt 97 17 proedgetradinggroup

    Any thoughts on how to fix this?

    Like

  4. Came across this when I tried to run the custom tool

    Error 1 Running transformation: The default target Entity Framework version requires the edmx schema version 2.0.0.0 or lower. The specified schema is version 3.0.0.0. To avoid this warning specify the target Entity Framework version explicitly. You can do this by using the EdmGen.exe command-line tool with the targetVersion option, or by including the targetEntityFrameworkVersion parameter when calling the GenerateCode method. 1 1

    Any ideas?

    Like

  5. […] Studio and Visual Studio Gallery to my sister. Then it was reported by a reader as a comment to the first post on view generation and code first I wrote some time ago. Then I saw it more and more often in search engine terms in the stats of […]

    Like

  6. Hi all,

    we have code first model with 325 entities.
    I pregenerated the view via T4 template for EF5.

    Constructor of the pregenerated view gets called, but GetViewAt no.
    I tried to place the code into all assemblies included in the project, but woth no luck.

    Please can you advice, what could be the problem?
    Init of the EF framewrok still takes about 40s and number of entities will grow.

    Thanks for help.

    Like

    • moozzyk says:

      Hi Ondrej,

      Are you using multiple entity containers or classes with pre-generated views or your entities live in multiple assemblies? I vaguely remember there was a case (bug) in EF5 where we would stop looking for views if we already found some even though we did not find all the views we needed. If you are able to provide me with a repro I could take a look at this.

      Thanks,
      Pawel

      Like

      • Hello Pawel,

        thanks for reply.
        Yes, we have multiple (at least 2) assemblies with domains. But we have only one domain context class.
        Approximate structure according to dependencies:
        – Core communication DLL
        – Core framework DLL (the context is defined here)
        – Core Domain DLL
        – some other domain DLL

        Repro project is hard to deliver.
        Do you know any workaround of the bug you mentioned?

        Thanks in advance

        Like

        • moozzyk says:

          Ahoj Ondrej,

          I talked to Arthur from our team (http://blog.oneunicorn.com/) and he said that the issue in EF5 was that we would stop looking for views if we found any. This would happen if views were split in multiple assemblies – we would find views in the first assembly and then would stop looking for views even though some of the views could live in another assembly. The workaround to this would be to use Metadataworkspace.LoadFromAssembly. I am not sure if this is what you are seeing since you said that in your case the ctor is being invoked but the GetViewAt method is not. Let me know if you have multiple models and multiple views spread across multiple assemblies.

          Pawel

          Like

          • Ahoj Pawel 🙂

            yes, we have multiple domain object assemblies.
            We have the only one complete model in separate assembly.
            Then we generate domain objects as like as controllers and views via custom T4 template.
            Derived DomainContext is in another assembly.

            If it is possible to use Metadataworkspace somehow, please can you describe some shrort solution or provide a sample? Unfortunatelly, the documentation is lacking the content.
            Some solutions are on stackoverflow, but I’m not sure this will fit our needs.

            Ondra

            Like

          • moozzyk says:

            Hi Ondrej,

            What I would try would be ((IObjectContextAdapter)ctx).ObjectContext.MetadataWorkspace.LoadFromAssembly({assemblyContainingViews}) where ctx is your DbContext derived class. If this does not help then it’s probably a different issue and it will be hard to me to tell what’s going on without being able to repro and debug the app. Alternatively – have you thought about trying EF6? There have been some improvements in the view gen code which resulted in significant performance gains and in many cases you no longer need to use pre-generated views anymore (in fact in EF6 beta, due to a bug, you would not even be able to use pre-generated views). As an additional benefit – since EF6 is open source if you hit a problem you can build and debug it to resolve the issue.

            Hope this helps,
            Pawel

            Like

  7. Will says:

    See this post
    http://stackoverflow.com/questions/1149290/ado-net-entity-framework-pre-generate-views
    I did the same test and found performance impoving not obvious
    What do you think of that?

    Like

    • moozzyk says:

      Are you talking about start up performance or performance in general? Is it EF5 or EF6? Pre-generated views help improving start up time but will not improve performance in general. If you are seeing some queries that take a long time you need to take a look at whether you are not querying for results you are not going to need (e.g. your query returns thousands entities but you actually need dozen), your query is very/overly complicated (it takes long to translate to SQL and makes EF generated an overly complicated SQL queries which take long to execute), your database is not optimized (e.g. you are not using indexes on the tables or the indexes you have are not optimal). If you are using EF6 – just before EF6 was shipped we found several performance regressions. Some of them were addressed on 6.0.1 but there are some outstanding ones. So for EF6 you need read this: http://blogs.msdn.com/b/adonet/archive/2013/10/31/ef6-performance-issues.aspx

      Like

      • Will says:

        Thank you for your response!I’m using EF5 in my project.My boss complained that application started up so slow.Therefore I tried pre-generated views,but the promotion was not obvious.
        Is that because most querys of the application are complicated ? Maybe I misunderstood and misuse the pre-generated views?

        Like

        • moozzyk says:

          Hi Will,

          Pregenerated views are supposed to address start up time. If you are using Code First however then model building may also contribute to slow start-up. I would first make sure that views are actually being used – set a breakpoing in the GetViewAt method in the code containing views and run with the debugger attached (F5) and make sure the breakpoint gets hit. If views are being used then it must be something else (e.g. when using CodeFirst it tries to connect to the database to check if your model matches the database – you may try disabling initializers if you have any to see if this can be the culprit). Btw. how big is your model and how long does the first query take? Are the queries huge/complicated?

          Thanks,
          Pawel

          Like

  8. Ahoj Pawel,

    I just want to inform you we moved to EF6 and startup times are much more better.
    So the EF6 is the only solution to the slow startup times.
    On the other side, EF6 has significant problems during migrations, they’re slow, so we moved to manual migrations control.

    Ondra

    Like

  9. Will says:

    Hi Pawel:

    Sorry for incomplete info before.Our team is using EF 5.0 Code First.The whole project is not big,about almost 200 entities and all of them have less than 30 properties.I tried debug the generated view.cs but it gets not hit.Finally,we use Glimpse to see the performance details,and find the bottleneck is not EF. But I still want to know how to disabling initializers in the project,because we also use Autofac ,the DBContext class is managed by Autofac,how can I make it? And will that be have side effects on the application?

    Thank you!
    Will

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: