When we’ve completed coding (and, if we’re feeling especially adventurous, testing said code), we promote our locally developed code to the Dev server. This serves as our integration environment. CruiseControl.Net watches our SCM for any changes promoted to the DEV branch, then it:
- Updates the configuration files to point to the Dev server instead of the developer’s local workstation;
- Builds our solution in Debug mode (for more verbose logging during the unit test runs);
- Runs the unit tests;
- Builds the solution again in Release mode;
- Runs the unit tests again to ensure they work in Release mode too; and
- Finally, publishes the necessary files to IIS.
Today, I need to figure out how much has to change to get this project onto our QA server.
The Problem
Pointing the QA environment to the QA database.
The biggest difference between the process described above and the process to move to QA is going to be changing the back end database that the solution points to. I.e. The Developer’s local workstation and the Dev server both point to the Dev database. (It’s an Oracle back end, so we do not have a separate local and server database — even if we wanted to, but I’m not sure we would.)
ORACLISMAn aside about Oracle schema ownership. As a SQL Server developer, there are several Oraclisms that confound me. I’m not sure that this is one of them, but here’s my take on it: In SQL Server, most databases are created by dbo and most tables are created as dbo. Security is then applied to users and groups to grant access to various tables. If you need to create a Dev database and a QA database for the Production database named AccountingSystemDB, you might create a new database, again as dbo, called AccountingSystemDEV and another called AccountingSystemQA. In Oracle, this notion of separate databases is superseded by a notion of separate schemas WITHIN a single database. There is no obvious corollary to dbo. The Oracle system user serves a very different purpose, perhaps relating more to the notion of sa in SQL Server. Instead, an Oracle DBA will create a Schema Owner (i.e. a user?) and then create the Schema as that owner, granting rights to that Schema to users. For example, the Oracle Server has a single Database instance, within which, you will see three schemas named ACC_SYS_DV, ACC_SYS_QA and ACC_SYS_P. Within each schema, you will find that schema’s version of the tables. I.e. ACC_SYS_DV.ACCOUNT, ACC_SYS_QA.ACCOUNT and ACC_SYS_P.ACCOUNT. Also, a lot of database apps, use a single Application User Id to access the database, relying on the Application to implement security. (Or at least, ours does.) For each of the three environments, this Application User logs in to one of the three Schemas. |
I am concerned that Entity Framework knows the Database and Schema in the connectionstring in the app.config for the ModelEF project:
... <edmKeys> <edmKey name="DataSource" connection="metadata=res://ModelEF/ModelEF.csdl| res://ModelEF/ModelEF.ssdl| res://ModelEF/ModelEF.msl; provider=Devart.Data.Oracle; provider connection string=" User Id=APPDV; Password=*****; Server=devTns.world; Home=oraodac11g_home1;Persist Security Info=True"" containerName="ModelEF.Entities"> ... </edmKey> ... </edmKeys> </ideablade.configuration>
Above we see the Oracle server instance devTns.world, and the Dev database schema owner APPDV. There is also a Home= entry pointing to the developer’s OraHome that needs to be removed.
EF then maps the Schema owner into the EDML (EDMX) file:
...<EntitySet Name="ACCOUNTs" EntityType="ModelEF.Store.ACCOUNT" store:Type="Tables" Schema="APPDV" <=NOTICE DV SCHEMA OWNER IS HERE Table="ACCOUNT" />...
If this was a SQL Server solution, the schema owner would be the same (whether it is dbo or not) in the AccountingSystemDEV and AccountingSystemQA databases. In Oracle, this is not the case, so I am curious if I will need to update the Schema tag in the EDML (EDMX) . If so, the promotion to QA will force a rebuild, not just a reconfigure.
The Ideablade configuration also knows the Database and Schema in the connection attribute of the edmKey tag in the Model project’s app.config and in the web host project’s web.config:
<configuration> <connectionStrings> <add name="Model.EFConnectionString" connectionString="metadata=res://*/ModelEF.csdl| res://*/ModelEF.ssdl| res://*/ModelEF.msl; provider=Devart.Data.Oracle; provider connection string=" User Id=APPDV; Password=*****; Server=devTns.world; Home=oraodac11g_home1; Persist Security Info=True"" providerName="System.Data.EntityClient" /> </connectionStrings> </configuration>
[It turns out, I do need to change the tags, change the connection strings, and regenerate the pregenerated views.]
A SOLUTION
Since we are using CruiseControl.Net (CCNet) for our automated build, and since we are using Nant for some of the less “build-ish” and more “configure-ish” tasks, I was able to add a Nant task in the CCNet project configuration that does a string replace in all the EDML (EDMX) and .config files, and then uses the T4 Template technique to recreate the Pregenerated Views.
In order to use the T4 Template attached to the ADO .Net blog, I had to make two changes because we are using Devart Entity Developer. First, the .tt file needs to be renamed from ModelEF.Views.tt
to ModelEF.PregeneratedViews.tt
. Second, the following line in the .tt file has to be changed:
string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".views", "") + ".edmx";
Should be replaced with:
string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".pregeneratedviews", "") + ".edml";
Here is the CCnet task in the CruiseControl.Net ccnet.config file to execute the Nant task:
<project name="PromoteToQA"> ... <tasks> <nant> <buildFile>ChangesBeforeBuild-QA.build</buildFile> <executable>E:\Program Files\nant-0.86-beta1\bin\nant.exe</executable> </nant> </tasks> ... </project>
And here is the Nant task itself:
<project name="ChangesBeforeBuild-QA" default="run"> <target name="run"> <call target="change Database" /> </target> <target name="change Database"> <foreach item="File" property="filename"> <in> <items> <include name="**/*.edml" /> <include name="**/app.config" /> <include name="**/web.config" /> <include name="**/*EF.Designer.cs" /> <include name="**/*EF.edps" /> </items> </in> <do> <attrib file="${filename}" normal="true" /> <loadfile file="${filename}" property="file.textContent"> <filterchain> <replacestring from="APPDV" to="APPQA" ignorecase="true"/> <replacestring from="*****" to="*****" /> <replacestring from="devTns.world" to="qaTns.world" ignorecase="true"/> <replacestring from="Home=oraodac11g_home1;" to="" /> </filterchain> </loadfile> <echo message="Writing Database Change to: ${filename}" /> <echo file="${filename}">${file.textContent}</echo> </do> </foreach> </target> <target name="Recreate Precompiled Views Files"> <!-- unlock the view files --> <foreach item="File" property="filename"> <in> <items> <include name="**/*.PregeneratedViews.cs" /> </items> </in> <do> <attrib file="${filename}" normal="true" /> </do> </foreach> <!-- transform the *.PregeneratedViews.tt files to recreate the *.PregeneratedViews.cs files --> <foreach item="File" property="filename"> <in> <items> <include name="**/*.PregeneratedViews.tt" /> </items> </in> <do> <echo message="Transforming: ${filename}" /> <exec program="dependencies\TextTransform\TextTransform.exe"> <arg value="${filename}"/> </exec> </do> </foreach> </target> </project>
And that’s it. Before CruiseControl.net starts running the actual tasks, it runs this Nant task to prepare the source files for the QA environment.