none
what would cause EF Code First IDatabaseInitializer InitializeDatabase to not get called? RRS feed

  • Question

  •  

    I have a custom initializer setup as follows:


    public class PromptIfChangesNeededDBInitializer : IDatabaseInitializer<MeyerREContext>
    {
    
        public PromptIfChangesNeededDBInitializer()
        {  // This constructor is called properly
    
        }
    
        #region IDatabaseInitializer<TContext> Members
    
    
        public void InitializeDatabase(MeyerREContext context)
        { // This is never called
    
           ... Code that checks existence and seeds etc
    
        }
    
     }


    Here is my DbContext class

     

    public class MeyerREContext : DbContext
    {
        static MeyerREContext()
        {
            Database.SetInitializer(new PromptIfChangesNeededDBInitializer());
        }
    
        public DbSet<Address> Addresses { get; set; }
        ... More DbSet property definitions
    
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
    
           modelBuilder.Configurations.Add(new AddressMap());
          ... More Configurations 
    
        }
    
    }


    The constructor of the Initializer is called properly as confirmed by a breakpoint, the OnModelCreating runs properly as confirmed by a breakpoint, but after the OnModelCreating is completed the InitializeDatabase is never called...

    Any ideas ?

    Thanks Greg

     

     


    • Edited by Greg Foote Monday, December 5, 2011 6:29 PM
    Monday, December 5, 2011 6:25 PM

Answers

  • Greg,

     

    Just wanted to let you know that the cause of the NullReferenceException has been identified and will be fixed in a future release of EF. The situation arises when a bi-directional association is configured from both sides with conflicting multiplicity, but the inverse navigation property is not specified on the configuration from one side even though it exists. For example:

     

    modelBuilder
        .Entity<SomeItem>()
        .HasOptional(d => d.Detail)
        .WithRequired(p => p.Item);

     

    modelBuilder
        .Entity<SomeItemDetail>()
        .HasRequired(d => d.Item)
        .WithRequiredPrincipal(); // <- There is an inverse nav-prop but it is not specified.

     

    Note that initialization of a DbContext instance is lazy so that if you don’t use the instance, then the cost of initializing it is never paid. This initialization will include building the model the first time the initialization happens. EF does not create any threads to do this—it happens in the thread that first uses the context instance. This

     
    post 

    has some more details of the way it works.

     

    Thanks,
    Arthur

    Wednesday, January 4, 2012 6:25 PM
    Moderator

All replies

  • Hi Greg;

    Have you tried calling this before any calls that creates the DbContext?

    Database.SetInitializer(new PromptIfChangesNeededDBInitializer());

    and remove it from the constructor in the DbContext.

     


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Monday, December 5, 2011 7:11 PM
  • Hi Fernando,

     

    That resulted in a different error... I think that the OnModelCreating is swallowing an exception somehow...  Here is the exception stack

       at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.ValidateConsistency(NavigationPropertyConfiguration navigationPropertyConfiguration)

    and the exception is a null reference exception...

    Thanks

    Greg

     

     

     

     


    Thanks, Greg
    Monday, December 5, 2011 7:33 PM
  • Hi Greg;

    Here is some sample code program that works with the Custom initializer. If you still having problem if you can post your model and the DbContext class I will build here and see if I can locate the issue.

    class PromptIfChangesNeededDBInitializer<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext
    {
        public void InitializeDatabase( TContext context )
        {
            var exists = context.Database.Exists( );
            if( exists && context.Database.CompatibleWithModel( true ) )
            {
                return;
            }
    
            if( exists )
            {
                Console.WriteLine( "The database exist but the model does not match" );
                Console.Write( "Do you wish to drop and recrate the database? (Y/N): " );
                
                var answer = Console.ReadKey( );
                Console.WriteLine( );
                if( answer.KeyChar == 'N' ) return;
    
                context.Database.Delete( );
            }
            context.Database.Create( );
        }
    }
    
    public class MeyerREContext : DbContext
    {
        public DbSet<Address> Addresses { get; set; }
    }
    
    public class Address
    {
        public int AddressId { get; set; }
        // First run leave commented out. On second run uncomment
        // public string Type { get; set; }
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string Ctiy { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
    }
    
    class Program
    {
        static void Main( string[ ] args )
        {
            // Register Custom initializer
            Database.SetInitializer( new PromptIfChangesNeededDBInitializer<MeyerREContext>( ) );
    
            InsertAddress( );
    
            Console.WriteLine("Press return to end.");
            Console.ReadLine( );
    
        }
    
        static public void InsertAddress()
        {
            var addr = new Address( );
    
            // First run leave commented out. On second run uncomment
            // addr.Type = "Work";
            addr.AddressLine1 = "Address Line 1";
            addr.AddressLine2 = "Address Line 2";
            addr.Ctiy = "The City";
            addr.Zip = "The Zip Code";
    
            using( var context = new MeyerREContext() )
            {
                context.Addresses.Add( addr );
                context.SaveChanges( );
            }
        }
    }
    

     


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Monday, December 5, 2011 8:53 PM
  • Fernando,

     

    Thanks for your help !!  That sample works ok...

    I have tracked the problem down to code that is executing after the OnModelCreating (my code) and before the model is created and ready to be initialized - hence thats why my initialize method never gets called...

    The problem is that EF spawns worker threads to create the Model and the NullReference Exception is happening inside one of those threads... But I can't get any detail on which of the configurations it is currently working on when it throws one of these exceptions ??

    System.NullReferenceException occurred
    Message=Object reference not set to an instance of an object.
    Source=EntityFramework
    StackTrace:
       at    
       
    System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.ValidateConsistency(NavigationPropertyConfiguration navigationPropertyConfiguration)

    Thanks

    Greg

     

     


    Thanks, Greg
    Monday, December 5, 2011 11:14 PM
  • Hi Greg;

    Sorry but that error message is kind of general but it seems like one of the configuration classes, navigationPropertyConfiguration, has an issue.


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Tuesday, December 6, 2011 4:21 AM
  • Fernando,

    I know its a configuration issue... But it doesn't happen until the OnModelCreating method is complete and the model is being constructed by EF...

    I need some way to get more information on the exception that happens in the

    ValidateConsistency(NavigationPropertyConfiguration navigationPropertyConfiguration)

    method...  I wish there was a method I could override to obtain notification of when an entity has finished being constructed or something !

    Thanks

    Greg

     

     

     

     


    Thanks, Greg
    Wednesday, December 7, 2011 2:17 PM
  • Hi,

    What if you take Fernando's code and tweak it until you repro the issue. It will be easier to see what happens with the *minimal* amount of code neeeded to repro your issue.

    Or does it happen if you move Database.SetInitializer to Main ? It could perhaps help to see if the issue is in the order in which things are called or if this is an issue caused by your model or whatever else...


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
    Wednesday, December 7, 2011 3:01 PM
  • Hi Greg;

    The only way to identify the offending line of code is to comment out all the navigation configuration code and compile and run. Then remove the comments one at a time compiling and running in between until you get the error back.

     


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".
    Wednesday, December 7, 2011 7:33 PM
  • The exception is *NOT* in my code...  Its in the Entity Framework code:

    had to install Reflector and Decompile the EF Assembly to find out what the problem was:

    This is the line that causes the problem...

     

    private void ValidateConsistency(NavigationPropertyConfiguration navigationPropertyConfiguration)
    {
     
    if ((navigationPropertyConfiguration.InverseEndKind.HasValue && this.EndKind.HasValue) && (navigationPropertyConfiguration.InverseEndKind != this.EndKind))
     
    {
       
    throw System.Data.Entity.ModelConfiguration.Resources.Error.ConflictingMultiplicities(this.NavigationProperty.Name, this.NavigationProperty.ReflectedType);
     
    }
     
    if ((navigationPropertyConfiguration.EndKind.HasValue && this.InverseEndKind.HasValue) && (navigationPropertyConfiguration.EndKind != this.InverseEndKind))
     
    {
       
    throw System.Data.Entity.ModelConfiguration.Resources.Error.ConflictingMultiplicities(this.InverseNavigationProperty.Name, this.InverseNavigationProperty.ReflectedType);
     
    }

     

    In my case the InverseNavigationProperty property is null which causes and exception WHEN EF is trying to throw an exception...

    The underlying problem is that I had an inverse property relationship defined but no mapping defined for it...

    However, it was impossible to acertain which entity was causing this problem... These checks in this method should be wrapped with a try catch to avoid this problem so they can throw a more meaningful error to the end user...

    Thanks Greg

     


    Thanks, Greg
    Wednesday, December 7, 2011 7:37 PM
  • Greg,

    Can you post a minimal stand alone repro for this? The NullReferenceException should not be happening. Having a repro would help investigate the cause. Also are you using EF 4.2?

    Thanks,

    Pawel

    Thursday, December 8, 2011 1:10 AM
  • Greg,

     

    Just wanted to let you know that the cause of the NullReferenceException has been identified and will be fixed in a future release of EF. The situation arises when a bi-directional association is configured from both sides with conflicting multiplicity, but the inverse navigation property is not specified on the configuration from one side even though it exists. For example:

     

    modelBuilder
        .Entity<SomeItem>()
        .HasOptional(d => d.Detail)
        .WithRequired(p => p.Item);

     

    modelBuilder
        .Entity<SomeItemDetail>()
        .HasRequired(d => d.Item)
        .WithRequiredPrincipal(); // <- There is an inverse nav-prop but it is not specified.

     

    Note that initialization of a DbContext instance is lazy so that if you don’t use the instance, then the cost of initializing it is never paid. This initialization will include building the model the first time the initialization happens. EF does not create any threads to do this—it happens in the thread that first uses the context instance. This

     
    post 

    has some more details of the way it works.

     

    Thanks,
    Arthur

    Wednesday, January 4, 2012 6:25 PM
    Moderator