Problem description
DBUpdater currently supports a single connection string. It parses the target application configuration file looking for a connection string named "connectionString". If the updater is called with the -connectionString parameter, this connection string is used instead (refer to DBUpdater - Provide the capability to pass a connection string as a parameter for more details). This connection string is assigned to the XafApplication.ConnectionString property and is then used to create a default object space provider. If the application uses multiple object space providers and connection strings for them are stored in the configuration file, the XafApplication.CreateDefaultObjectSpaceProvider method usually looks as follows:
C#protected override void CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
args.ObjectSpaceProviders.Add(
new XPObjectSpaceProvider(ConfigurationManager.ConnectionStrings["ConnectionStringXpo"].ConnectionString, null));
args.ObjectSpaceProviders.Add(
new EFObjectSpaceProvider(typeof(MyDbContext),
ConfigurationManager.ConnectionStrings["ConnectionStringEF"].ConnectionString));
}
This approach is described in the eXpressApp Framework > Task-Based Help > How to: Use Both Entity Framework and XPO in a Single Application topic and this code is automatically generated by the XAF Solution Wizard when both XPO and EF are selected.
In this scenario, DBUpdater cannot initialize the databases because connection strings with other names are not extracted from the application configuration file and the updater throws NullReferenceException on the application setup (see T234726). This happens because when invoked from the DBUpdater context, the ConfigurationManager refers to the updater's config file where no connection strings are declared.
Current solutions
Make sure that you register providers in the CreateDefaultObjectSpaceProvider method or in the CreateCustomObjectSpaceProvider event handled in the application or module class. If the CreateCustomObjectSpaceProvider event is handled outside the application or module instance (e. g., in Program.cs), it won't work because the Program class code is not executed by the updater. Then, use any of the following solutions.
1. Stop using System.Configuration.ConfigurationManager in the CreateDefaultObjectSpaceProvider method and CreateCustomObjectSpaceProvider event handler. Instead, specify the connection string directly as follows:
new XPObjectSpaceProvider("your connection string for XPO here", null));
2. Temporarily modify the DBUpdater\DBUpdater.v14.2.exe.config file by copying the <connectionStrings/> section from the XAF application's configuration file. For example:
XML<connectionStrings>
<add name="ConnectionStringXpo" connectionString="Integrated Security=SSPI;Pooling=false;MultipleActiveResultSets=True;Data Source=.;Initial Catalog=XpoDatabase"/>
<add name="ConnectionStringEF" connectionString="Integrated Security=SSPI;Pooling=false;MultipleActiveResultSets=True;Data Source=.;Initial Catalog=EFDatabase"/>
</connectionStrings>
3. Modify the DBUpdater source code (DevExpress.ExpressApp.Tools\DBUpdater\Program.cs) to support multiple connection strings in the manner you specify them in your XAF application and build a custom updater.
Our initial research has shown that this functionality was not implemented in the DBUpdater tool from the beginning because of the difficulty in identifying the correct connection string in a case when application sets up several ObjectSpaceProviders, e.g., one for XPO and one for EF data model.
By default, the DBUpdater can take the connection string from the configuration file (by a predefined connection string name) and from the XafApplication object itself. However, in general, you may want to supply a connection string at runtime (right when you create a certain ObjectSpaceProvider).
When doing this, you rarely hard-code a connection string value, but rather refer to the ConfigurationManager object to obtain a value by a key from the configuration file.
However, in this case the DBUpdater, as any other external application, will fail accessing ConfigurationManager as it will refer to its own configuration file. This situation can be simplified if all ObjectSpaceProviders refer to the same database and thus there will be only one connection string (it can be hard-coded or obtained from the configuration file in the XafApplication's constructor), but we are not sure if this covers the majority of cases to implement it by default.
So, the question is: how do you wish to setup the DBUpdater in this case? Also, do you usually connect to the same application database or can there be a different database in a general case? Your opinion on this is much appreciated.
actually i connect to the same database. but the connection string is different.
this is my xpo connection string:
Data Source=.\sqlexpress;Pooling=false;Initial Catalog=MyCatalog;Persist Security Info=True;User ID=sa;Password=******
and my entity connectionstring is:
metadata=res://*/BusinessObjects.EDMX.GpsManagerXafModel.csdl|res://*/BusinessObjects.EDMX.GpsManagerXafModel.ssdl|res://*/BusinessObjects.EDMX.GpsManagerXafModel.msl;provider=System.Data.SqlClient;provider connection string="data source=.\sqlexpress;initial catalog=MyCatalog;user id=sa;password=******;MultipleActiveResultSets=True;App=EntityFramework"
Is it possible for me to modify the DBUpdater to hard code my own connectionStrings for my project.
Will the dbupdater be able to access both of my objectspaces (EF et XPO) if i do so?
thank you
@Dominic: Thank you for your feedback.
Yes, it is possible to do so since you own the updater's source code:
C:\Program Files (x86)\DevExpress 13.2\Components\Sources\DevExpress.ExpressApp.Tools\DBUpdater\
Take special note of the current code that uses only the first ObjectSpaceProvider:
DatabaseUpdater dbUpdater = application.CreateDatabaseUpdater(application.ObjectSpaceProviders[0]);
You need to rework this code to iterate through the application.ObjectSpaceProviders collection and then call update routine for each DatabaseUpdater object:
IList<CompatibilityError> errors = dbUpdater.CheckCompatibilityForAllModules(); if(errors != null && errors.Count > 0) { exitCode = UpdateDataBase(errors, dbUpdater, silent); } else { Console.WriteLine(updateNotNeeded + "."); exitCode = ExitCode.UpdateNotNeeded; }
I hope this helps.
when I try this
DatabaseUpdater dbUpdater2 = application.CreateDatabaseUpdater(application.ObjectSpaceProviders[1]);
which is my efObjectSpaceProvider it returns null
any ideas ?
@Dominic:
It all looks like your DbContext descendant does not have the following member in it:
public DbSet<DevExpress.ExpressApp.EF.Updating.ModuleInfo> ModuleInfo { get; set; }
I hope this helps. Otherwise, please create a separate ticket and attach your project there.
i've added the following line in my context.tt file ( i use model first with entity framework 6):
public virtual DbSet<DevExpress.ExpressApp.EF.Updating.ModuleInfo> ModuleInfo { get; set; }
but when i try to log in to the app i have a error which is:
The entity set for the 'DevExpress.ExpressApp.EF.Updating.ModuleInfo' type is not found.
here is my log file
any ideas ?
@Dominic: I checked your log file, but it is not quite clear why this error occurred. Please create a separate ticket in the Support Center and attach your project there so I can research it locally. Thanks.