2

Closed

DbConfiguration.SetInitializer Reference Context Problem

description

I'm unable to use the SetInitializer method of EF6 DbConfiguration. Here's the problem I'm encountering.

I have one project for my model, HotelRoomsModel, which inherits DbContext.
I'm using Migrations so in the same project, I have a Configuration file.

In a second project I created a DbConfiguration class and one of the configuraitons is to set the database initializer for my model.

namespace DataLayer.DbConfigurations
{
public class CustomDbConfiguration : DbConfiguration
{
public CustomDbConfiguration()
{
  SetDefaultConnectionFactory(new LocalDbConnectionFactory("v11.0"));
  SetDatabaseInitializer(new MigrateDatabaseToLatestVersion
                                        <HotelRoomsModel, HotelModel.HotelMigrations.Configuration>());
 }
}
}

In order to specify the context, this project needs a reference to the model's project.

Now I need to "trigger" the configuration. One method as per your overview is to use an attribute in the DbContext. However, that would require the model project to have a reference to the DbConfiguration project which would create a circular reference.

So that's problem #1 because my understanding of the specs and the overview samples was that I should be able to do this.

The alternate suggestion is to specify the DbConfiguration in the app.config file.

<entityFramework codeConfigurationType="DataLayer.DbConfigurations.CustomDbConfiguration,DbConfigurations"/>

I have yet another project which is a console app. In the main method all I'm doing is initializing my model.

If I run without debugging, I get a stackoverflow exception.

If I debug I can see it hit the code as follows:

1) Instantiate HotelsRoomsModel class
2) Instantiate CustomDbConfiguration class
3) SetDefaultCOnnetionFactory in CustomDbConfiguraiton
4) SetDatabaseInitalizer in CustomerDbConfiguration
5) Then it seems to recursively instantiate this class over and over until it overflows.

I stopped it before it overflowed even then the inellitrace file I collected is over 16MB and too big to attach.

Can you easily reproduce it? Would you like me to attach a solution? Or am I doing something very obviously wrong that you can see in the description above?

file attachments

Closed Jan 9 at 6:59 PM by lukew
This fix is in the nightly build 6.1.0-alpha1-30109

comments

ajcvickers wrote Nov 20, 2012 at 4:19 PM

"One method as per your overview is to use an attribute in the DbContext. However, that would require the model project to have a reference to the DbConfiguration project which would create a circular reference."

You can specify the DbConfiguration type using a string instead of the actual type. This will then not require a reference to the project that contains the context. Also, can I ask why use chose to not locate the DbConfiguration in the same assembly as the context?

Can you please provide a repro for the second problem.

jlerman wrote Nov 30, 2012 at 5:12 PM

Thanks Arthur. I've replied in email re a report. About the separate dbconfiguration...I have multiple contexts that live in separate projects. If I have a common configuration to share I want that in a separate project. And the specs suggested that this was supported so naturally I had to try it out! :)

jlerman wrote Dec 15, 2012 at 12:54 PM

thanks. I had also tried the string overload. I will go through all of my attempts again and then send you the project.

ajcvickers wrote Feb 18, 2013 at 11:21 PM

Pinged Julie again by email.

jlerman wrote Feb 19, 2013 at 1:47 AM

out of sight out of mind! Attached.

ajcvickers wrote Feb 19, 2013 at 5:47 PM

EnsureLoadedForContext is called from various places as soon as a context type is known to ensure that the correct DbConfiguration is found. However, sometimes the derived DbConfiguration itself can make use of a context type (such as when the Migrations initializer is used) in which case this would cause recursive calls into EnsureLoadedForContext resulting in stack overflow. The solution is to flag that the assembly has been visited at the start of the call instead of the end.

jlerman wrote Feb 19, 2013 at 6:31 PM

ahh okay so I wasn't doing it wrong? or was there a better way I should have set this up?

I'm also curious about the circular reference I ran into trying to set this up with attributes. Any ideas?

thanks

ajcvickers wrote Feb 20, 2013 at 6:02 PM

Update: Brice recognized that the previous iteration introduced a race condition which would have allowed an app to use configuration before it was properly configured. Second iteration changes EnsureLoadedForContext to only create the type rather than the instance such that the change that introduced the race condition is not now needed.

ajcvickers wrote Feb 20, 2013 at 6:39 PM

Commit: f344923ea335

RoMiller wrote Feb 27, 2013 at 11:30 PM

Verified by Mugdha

** Closed by RoMiller 02/27/2013 4:30PM

jlerman wrote Oct 10, 2013 at 7:24 PM

bad news...having hte same problem with current nightly build when I'm trying to do migrations from nuget.

example:
I have a project with my model.
I have a separate project with the custom dbconfiguration.
I ahve anotehr project with an executable (say console or tests)

My normal m.o. is to have package mgr console pointing to the model project when I run migration commands.

In my executable project (which is my startup project) I have this in the config file:

<entityFramework codeConfigurationType="DataLayer.DbConfigurations.CustomDbConfiguration,DbConfigurations">

If I try add-migration, I get an error that the DbCOnfigurations assembly can't be loaded. I can't make a reference to it from the model project because it's a circular ref (that dbconfig project refs the model project).

I've tried starting with my test project which does have a reference then specifying

add-migration someMigration -ProjectName:MyModelNamespace.MyModel

same error...DbConfigurations assembly can't be loaded

I even tried pointing the package mgr console to the project with DbConfigurations and get the same results.

I finally succeed by physically copying the DbConfigurations.dll file (and another custom dll that that project depdns on) into the bin folder of my model project.

Do you think this is the best solution or am I missing something obvious that I should be doing instead? Is there, for example, a way to specify the physical file location as a parameter of (or alternate to) the codeConfigurationType attribute?

Thanks!

julie

jlerman wrote Oct 10, 2013 at 7:24 PM

"migrations from nuget" ...should be from nuget's package manager console window. :)

ajcvickers wrote Oct 10, 2013 at 8:21 PM

Re-opening for triage.

glennc wrote Oct 11, 2013 at 6:25 PM

EF Team Triage: Assigning to team member to investigate

ajcvickers wrote Oct 14, 2013 at 9:41 PM

Note for triage: The problem here is in the way the app-domain is setup to enable migrations. The powershell command uses the install path of the target project as the base directory for assembly resolution. But in this case the target project doesn't have any reference to the project the contains the DbConfiguration, so Visual Studio never deploys the configuration assembly to this location.

The fix is probably to use the startup project install path for app-domain assembly resolution. However, this could fail if you don't want your running app to have a reference to your migrations assembly. This means it may need to be configurable, or it may even require an attempt to pull in assemblies from other places manually.

jlerman wrote Oct 14, 2013 at 10:34 PM

Hey Arthur, I know the note is for triage not me but I have another idea. Since this problem seems specific to using the powershell commands, would it be possible to have a parameter on update-database that allows you to do something like provide the file path of any other needed assemblies or even some flag to say "load all of the referenced dlls from project x"?

ajcvickers wrote Dec 28, 2013 at 1:42 AM

Fixed in ce6bdc940ae7

Migrating without a map (Fixes for 671 and 1752)

This change adds options to the Migrations commands to allow some more flexibility in the way projects/references are setup. For 1752 the problem was that the context type had to be in a project of the solution as opposed to being in a referenced assembly. The solution is to allow the context assembly name to be specified as an alternative to the context project. In the common case this is the only change needed since the Migrations project references the context assembly and hence is discoverable by the app domain. For example:

PM> enable-migrations -ContextAssemblyName MyLibrary
Checking if the context targets an existing database...
Code First Migrations enabled for project ConsoleApplication11.

PM> add-migration First
Scaffolding migration 'First'.

PM> update-database
Applying explicit migrations: [201312272009380_First].
Applying explicit migration: 201312272009380_First.
Running Seed method.

The fix for 671 is more general and allows an explicit directory to be passed as the base directory used by the Migrations app domain. I verified that this works for the scenario reported in 671 using something like this:

PM> enable-migrations -StartUpProjectName ConsoleApplication10 -AppDomainBaseDirectory "C:\stuff\ConsoleApplication10\ConsoleApplication10\bin\Debug"
Checking if the context targets an existing database...
Code First Migrations enabled for project MyModel.

PM> add-migration First -StartUpProjectName ConsoleApplication10 -AppDomainBaseDirectory "C:\stuff\ConsoleApplication10\ConsoleApplication10\bin\Debug"
Scaffolding migration 'First'.

PM> update-database -StartUpProjectName ConsoleApplication10 -AppDomainBaseDirectory "C:\stuff\ConsoleApplication10\ConsoleApplication10\bin\Debug"
Applying explicit migrations: [201312272006090_First].
Applying explicit migration: 201312272006090_First.
Running Seed method.

It should also work for more advanced cases of 1752 where the assembly with the context somehow is not referenced directly by the Migrations project.

Testing was done manually. I looked at adding unit tests, but there currently aren't any tests for this code and it seemed prohibitively expensive to add them at this time.

jlerman wrote Dec 28, 2013 at 3:31 PM

Hooray and ...wait a minute! Do you mean my suggestion is what you implemented for the fix? faints :) THANKS! I'll try out a nightly build soon!