November Happy Hour will be moved to Thursday December 5th.
November Happy Hour will be moved to Thursday December 5th.
Hi,
Not sure how exactly Epi 5 was built around configuration and programmatic manipulation of the data.
But when it comes to EPi v7 -> eventually code will reach EPiServer.Data.Configuration.EPiServerDataStoreSection class which will ask ConfigurationManager to open mapped exe configuration file - ConfigurationManager.OpenMappedExeConfiguration() to look for connection information in current app domain config file - which means that connection string will be searched in web.config or any external config file mentioned as source for connection strings.
Can you share some existing code fragments for adding connection strings manually?
Just curious - what was the reason behind decision - "they do not use any connectionStrings.config files at all" ?
Hi,
This is the EPiServer 5 code which added the connectionstring: (in Global.asax)
protected void Application_Start()
{
AddConnectionString("EPiServerDB", CONNECTIONSTRING_READ_FROM_EXTERNAL_FILE_HERE);
}
protected static void AddConnectionString(string ConnectionName, string ConnectionString)
{
var csSetting = new ConnectionStringSettings(ConnectionName, ConnectionString, "System.Data.SqlClient");
var readonlyField = typeof(ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
readonlyField.SetValue(ConfigurationManager.ConnectionStrings, false);
var baseAddMethod = typeof(ConfigurationElementCollection).GetMethod("BaseAdd", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(ConfigurationElement) }, null);
baseAddMethod.Invoke(ConfigurationManager.ConnectionStrings, new object[] { csSetting });
readonlyField.SetValue(ConfigurationManager.ConnectionStrings, true);
}
The reason for not using the connectionstrings.config, is due to the structure of the project.
The client has an IT department which is responsible for the business logic, while we are responsible
for the website. Without going into too many details, the client's IT department is responsible for maininting an external
file which contains all connectionstrings. It is from this file we read the connectionstring to episerver.
Since you were already outside the realm of properly supported solutions (with the reflection-based config changes) I think something like this may be a better option even though essentially all the classes involved are marked as "This class supports the EPiServer infrastructure and is not intended to be used directly from your code.":
Make your initialization module an IConfigurableModule, have it depend on DataInitialization ([ModuleDependency(typeof(DataInitialization))]).
In the ConfigureContainer method do something like this:
public virtual void ConfigureContainer(ServiceConfigurationContext context)
{
context.Container.Configure(ce =>
{
ce.For<IDatabaseFactory>().Singleton().Use<MyOwnDatabaseFactoryWhichDoesNotReadTheConfigFile>();
}
}
Then implement your MyOwnDatabaseFactoryWhichDoesNotReadTheConfigFile (IDatabaseFactory) which does what you need, possibly basing it on the regular SqlDatabaseFactory. The method CreateDefaultHandler is of particular interest.
It sure seems like it requires a bit usage of reflector to get this working properly.
Thank you for your reply, though!
Oh right, forgot about factory.
This should work fine:
public class MySqlDatabaseFactory : SqlDatabaseFactory
{
public new IDatabaseHandler CreateDefaultHandler()
{
var settings = GetCurrentSiteSettings();
return new SqlDatabaseHandler(new ConnectionStringSettings
{
ConnectionString = "..." // retrieved from external service
},
settings.DeadlockRetries,
settings.DeadlockRetryDelay,
settings.DatabaseQueryTimeout);
}
}
Yes, something very similar* to what Valdis suggested should work for overriding how the database handlers used by EPiServer are instantiated.
(On the other hand any non-EPiServer things would still need a solution. If you want to go with the original approach I think the key for that to work would be to have your code execute earlier.)
*) When I saw the method hiding going on I realized that there was a problem which could be confusing.
As is implied by the IoC container configuration where we configure what implementation to use for IDatabaseFactory, the calling code is operating on the IDatabaseFactory interface which means that only hiding SqlDatabaseFactory.CreateDefaultHandler using "new" will not actually change the behavior, the calls will still go to the interface implementation in SqlDatabaseFactory.
If you were to do something like the following (key difference here is the ", IDatabaseFactory" bit) I believe that should lead to the desired results:
public class MyOwnDatabaseFactoryWhichDoesNotReadTheConfigFile : SqlDatabaseFactory, IDatabaseFactory
{
public new IDatabaseHandler CreateDefaultHandler()
{
var connectionString = new ConnectionStringSettings("name", "connection string", "System.Data.SqlClient");
return CreateHandler(connectionString);
}
}
Almost there......I've implemented your suggested code so far and it seems to be the correct way to go. I dont get the exception anymore, but when I try to login to EPiServer, I just recieve a blank page. No errors, nothing.
My code looks like this:
public class MyDatabaseFactory : SqlDatabaseFactory, IDatabaseFactory
{
public new IDatabaseHandler CreateDefaultHandler()
{
var connectionString = new ConnectionStringSettings("EPiServerDB", "CONNECTIONSTRING_HERE", "System.Data.SqlClient");
return CreateHandler(connectionString);
}
}
[ModuleDependency(typeof(DataInitialization))]
public class ConnectionStringInitialization : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Container.Configure(ce =>
{
ce.For<IDatabaseFactory>().Singleton().Use<MyDatabaseFactory>();
});
}
public static ConnectionStringInitialization Instance
{
get;
private set;
}
#region IInitializableModule Members
public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context)
{
Instance = this;
}
public void Preload(string[] parameters)
{
//throw new NotImplementedException();
}
public void Uninitialize(EPiServer.Framework.Initialization.InitializationEngine context)
{
Instance = null;
}
#endregion
}
I'll update this post when I get an idea of what is going wrong.
Hi!
Our client is running an EPiServer 5 CMS site, where we add connectionstrings in the Global.asax Application_Start() method, ie. they do not use the ConnectionStrings.config file at all.
We are in the process of upgrading/re-creating the website as an EPiServer 7 solution and with the new initialization-engine, I thought it was just a matter of moving this code to an InitializationModule. When I debug my init-module, I can see that the connectionstrings are added correctly, but I still recieve this Exception:
No connection string found with the configured name 'EPiServerDB'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Configuration.ConfigurationErrorsException: No connection string found with the configured name 'EPiServerDB'.
Does anyone have any experience with adding ConnectionStrings (more importantly 'EpiserverDB' connectionstring) programmatically in EpiServer 7 who can give me some tips on how to solve this?
Best regards
Jesper