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.
- Entity Framework code first custom connection string and migrations
- Changing connection string at runtime
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.