1

At the moment I am trying to use the code first approach of the entity framework 6 to create / connect to different databases on an SQL Express 2012 instance. The connection string to the database needs to be created in code and if the database does not exist it should be created on the SQL Express instance. Below is my class structure:

    public class TestContext : DbContext
    {
        public TestContext()
        {}

        public TestContext(string connectionString)
            : base(connectionString)
        {
         Database.SetInitializer(new MigrateDatabaseToLatestVersion<TestContext, Configuration>());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<ChildItem>().HasRequired(s => s.Parent)
            .WithMany(s => s.ChildItems);
            base.OnModelCreating(modelBuilder);

        }

        public DbSet<ParentItem> Parents { get; set; }
        public DbSet<ChildItem> Childs { get; set; }
    }

    [Table("Parents")]
    public class ParentItem
    {
        public ParentItem()
        {
            Childs = new List<ChildItem>();
        }

        [Key]
        public int ParentId {get; set;}
        public string ParentName { get; set; }

        public virtual List<ChildItem> Childs { get; set; }
    }

    [Table("Childs")]
    public class ChildItem
    {    
        [Key]
        public int ChildId { get; set; }
        public string Name { get; set; }
        public virtual ParentItem Parent { get; set; }
    }

When I now try to add Data for a new database or add data to an existing one I receive the following exception.

> System.Data.DataException was unhandled by user code  
> HResult=-2146233087   Message=An exception occurred while initializing
> the database. See the InnerException for details.  
> Source=EntityFramework   StackTrace:
>        at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action
> action)
>        at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
>        at System.Data.Entity.Internal.LazyInternalContext.<InitializeDatabase>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.Initialize()
>        at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type
> entityType)
>        at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
>        at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
>        at System.Data.Entity.Internal.Linq.InternalSet`1.ActOnSet(Action action,
> EntityState newState, Object entity, String methodName)
>        at System.Data.Entity.Internal.Linq.InternalSet`1.Add(Object entity)
>        at System.Data.Entity.DbSet`1.Add(TEntity entity)
>        at UnitTestProject.TestContextTest.InsertManyMeasurmentsDirectlyWithSql()
> in MSSQLTest.cs:line 123   InnerException:
> System.Data.Entity.Core.ProviderIncompatibleException
>        HResult=-2146233087
>        Message=An error occurred while getting provider information from the database. This can be caused by Entity Framework using an
> incorrect connection string. Check the inner exceptions for details
> and ensure that the connection string is correct.
>        Source=EntityFramework
>        StackTrace:
>             at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices
> providerServices, DbConnection connection)
>             at System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.<>c__DisplayClass1.<ResolveManifestToken>b__0(Tuple`3
> k)
>             at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey
> key, Func`2 valueFactory)
>             at System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.ResolveManifestToken(DbConnection
> connection)
>             at System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(DbConnection
> connection, DbProviderManifest& providerManifest)
>             at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
>             at System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(DbContext
> context, XmlWriter writer)
>             at System.Data.Entity.Utilities.DbContextExtensions.<>c__DisplayClass1.<GetModel>b__0(XmlWriter
> w)
>             at System.Data.Entity.Utilities.DbContextExtensions.GetModel(Action`1
> writeXml)
>             at System.Data.Entity.Utilities.DbContextExtensions.GetModel(DbContext
> context)
>             at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration
> configuration, DbContext usersContext)
>             at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration
> configuration)
>             at System.Data.Entity.MigrateDatabaseToLatestVersion`2.InitializeDatabase(TContext
> context)
>             at System.Data.Entity.Internal.InternalContext.<>c__DisplayClasse`1.<CreateInitializationAction>b__d()
>             at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action
> action)
>        InnerException: System.Data.Entity.Core.ProviderIncompatibleException
>             HResult=-2146233087
>             Message=The provider did not return a ProviderManifestToken string.
>             Source=EntityFramework
>             StackTrace:
>                  at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection
> connection)
>                  at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices
> providerServices, DbConnection connection)
>             InnerException: System.Data.SqlClient.SqlException
>                  HResult=-2146232060
>                  Message=A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was
> not found or was not accessible. Verify that the instance name is
> correct and that SQL Server is configured to allow remote connections.
> (provider: SQL Network Interfaces, error: 26 - Error Locating
> Server/Instance Specified)
>                  Source=.Net SqlClient Data Provider
>                  ErrorCode=-2146232060
>                  Class=20
>                  LineNumber=0
>                  Number=-1
>                  Server=""
>                  State=0
>                  StackTrace:
>                       at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException
> exception, Boolean breakConnection, Action`1 wrapCloseInAction)
>                       at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject
> stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
>                       at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo,
> SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout,
> Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean
> integratedSecurity, Boolean withFailover)
>                       at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo
> serverInfo, String newPassword, SecureString newSecurePassword,
> Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean
> withFailover)
>                       at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo
> serverInfo, String newPassword, SecureString newSecurePassword,
> Boolean redirectedUserInstance, SqlConnectionString connectionOptions,
> SqlCredential credential, TimeoutTimer timeout)
>                       at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer
> timeout, SqlConnectionString connectionOptions, SqlCredential
> credential, String newPassword, SecureString newSecurePassword,
> Boolean redirectedUserInstance)
>                       at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity
> identity, SqlConnectionString connectionOptions, SqlCredential
> credential, Object providerInfo, String newPassword, SecureString
> newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString
> userConnectionOptions, SessionData reconnectSessionData)
>                       at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions
> options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo,
> DbConnectionPool pool, DbConnection owningConnection,
> DbConnectionOptions userOptions)
>                       at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool
> pool, DbConnection owningObject, DbConnectionOptions options,
> DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
>                       at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection
> owningObject, DbConnectionOptions userOptions, DbConnectionInternal
> oldConnection)
>                       at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection
> owningObject, DbConnectionOptions userOptions, DbConnectionInternal
> oldConnection)
>                       at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection
> owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean
> allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions
> userOptions, DbConnectionInternal& connection)
>                       at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection
> owningObject, TaskCompletionSource`1 retry, DbConnectionOptions
> userOptions, DbConnectionInternal& connection)
>                       at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection
> owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions
> userOptions, DbConnectionInternal oldConnection, DbConnectionInternal&
> connection)
>                       at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection
> outerConnection, DbConnectionFactory connectionFactory,
> TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
>                       at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection
> outerConnection, DbConnectionFactory connectionFactory,
> TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
>                       at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1
> retry)
>                       at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1
> retry)
>                       at System.Data.SqlClient.SqlConnection.Open()
>                       at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass2f.<UsingConnection>b__2d()
>                       at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.<Execute>b__0()
>                       at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1
> operation)
>                       at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action
> operation)
>                       at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection
> sqlConnection, Action`1 act)
>                       at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection
> sqlConnection, Action`1 act)
>                       at System.Data.Entity.SqlServer.SqlProviderServices.GetDbProviderManifestToken(DbConnection
> connection)
>                       at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection
> connection)
>                  InnerException:

I have found out that the reason for this is that the TestContext.Parents.Add(..) calls the default constructor of my TestContext and that one does not call a base constructor. I have found a very dirty workaround for this which looks like the following.

public class TestContext : DbContext
    {
        private static string dbname = "TestDB";

        public TestContext() : base(dbname)
        {
        }

        public TestContext(string connectionString)
            : base(connectionString)
        {
            dbname = connectionString;
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<TestContext, Configuration>());
        }

After this every things works fine but I do not like this solution. Especially when I will work with multiple threads supporting multiple database I will ran into a problem with the static variable. If have already tried the solutions from the following sites but nothing had worked.

So has anyone else some suggestions how this could be solved?

--- Edit: 24.03 ----

further investigations has shown that the described problem and the second call of the default constructor only occurs when the migration is enabled.

Community
  • 1
  • 1

1 Answers1

0

I have found a solution or better a workaround I can live with. To solve the problem I use multiple database context to access the database. I will have on database context which reflects the complete database structure. For this database context the migration feature will be enabled. This database context will be used to create the database. With this limitation I can use the following code for the first database creation. But I need to be sure that only one thread at the time creates a new database.

public class TestContext : DbContext
{
  private static string connection = "TestDb";

  public TestContext() : this(connection)
  {}

  public TestContext (string newConnection) : base(connection)
  {
    connection = newConnection;
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<TestContext, Configuration>());
  }
…

The other database contexts are accessing only a part of the database system but set the database initializer to null. With this I can access multiple databases with dynamic strings and do not have the problems with the default constructor. I also can access the databases with different threads without worring about the static variable.

public class SecondTestContext : DbContext
{
  public SecondTestContext (string dbName) : base(dbName)
  {
    Database.SetInitializer<SecondTestContext>(null);
  }
…