My favorite ORM for .NET development is by far .netTiers (http://nettiers.com/) – a set of templates for CodeSmith (http://www.codesmithtools.com/) that create an awesome Business Logic Layer (BLL) and a Data Access Layer (DAL) in seconds.
The .netTiers Application Framework: http://nettiers.com/
.netTiers uses a connection string to connect to a SQL Server database at runtime – it works with an on-premise SQL Server (e.g. SQL Server Express 2008) or a SQL Azure database in the cloud. Out of the box, .netTiers reads this connection string from a Web.config (or app.config) file.
Since Windows Azure has its configuration in ServiceConfiguration.cscfg, I modified the .netTiers 2.3 templates so at runtime our DAL reads the connection string from the correct place.
Thanks to SuperJeffe who gave me the pointer in this thread on what changes were needed. Below you can find my notes on what changes were made to the .netTiers templates and you can download the modified templates as well.
Modifying .netTiers templates to support Windows Azure configuration
Download files:If using the Windows Azure SDK from November 2009 or later, download the modified .netTiers 2.3 templates here:
http://www.ehuna.org/files/NetTiers-Support-Windows-Azure-SDK-200911.zip
If using the Windows Azure SDK from October 2009 or earlier, download the modified .netTiers 2.3 templates here:
http://www.ehuna.org/files/NetTiers-Support-Windows-Azure.zip
If you use these modified .netTiers 2.3 templates, it is very easy to get your DAL working in Windows Azure. Once you download the modified templates and follow the installation instructions below, you’ll see I modified the .netTiers templates and added a "IsConnectionStringAzure" property:
<%@ Property Name="IsConnectionStringAzure" Type="System.Boolean" Default="False" Category="02. Framework Generation - Optional" Description="Indicates if at runtime the netTiersConnectionString is read from Window Azure's ServiceConfiguration.cscfg or from Web.config/app.config." %>
Just set "IsConnectionStringAzure" to "true" (the default is "false") and at runtime, your DAL will read from Windows Azure configuration instead of web.config (or app.config). When creating a new Windows Azure project, add this to your ServiceDefinition.csdef file:
<ConfigurationSettings>
<Setting name="netTiersConnectionString" />
</ConfigurationSettings>
And then in your ServiceConfiguration.cscfg add this:
<ConfigurationSettings>
<Setting name="netTiersConnectionString" value="Server=tcp:MYSQLAZURESERVER.ctp.database.windows.net;Database=master;User ID=MYLOGIN;Password=MYPASSWORD;Trusted_Connection=False;" />
</ConfigurationSettings>
Your netTiersConnectionString can be your regular connection string that point to a local on-premise SQL Server database (for development), or a SQL Azure database when you deploy to the Windows Azure cloud (Staging or Production).
I attach the changes to the .netTiers 2.3 templates I made and below I describe in detail the actual changes.
I also have a series of videos I've created that cover Windows Azure, SQL Azure and how to use .netTiers with both – you can see the index here:
Windows Azure and SQL Azure Videos
http://blog.ehuna.org/2009/09/windows_azure_and_sql_azure_vi.html
Thanks to my co-workers, Arif and Lanh, who helped me think through the steps we needed to implement to make this work.
Good times!
Emmanuel Huna
Check out our personal site at http://www.ehuna.org
Follow me on http://twitter.com/ehuna
Download files:
If using the Windows Azure SDK from November 2009 or later, download the modified .netTiers 2.3 templates here:
http://www.ehuna.org/files/NetTiers-Support-Windows-Azure-SDK-200911.zip
If using the Windows Azure SDK from October 2009 or earlier, download the modified .netTiers 2.3 templates here:
http://www.ehuna.org/files/NetTiers-Support-Windows-Azure.zip
Info on NetTiers-Support-Windows-Azure-SDK-200911.zip (Azure SDK 2009/11 or later)
Here's what you'll find in the ZIP file:
01-readme.txt - this file.
02-NetTiers-Azure-SDK-200911-Support-Template-Changes.txt - a log of the changes I made to the .netTiers 2.3 templates so out of the box your data access layer works with Windows Azure November 2009 SDK configuration (ServiceConfiguration.cscfg).Inside the "NetTiers" folder you find:
\NetTiers.cst - changes to the main .netTiers template
\NetTiers\DataAccessLayer\DataRepository.cst - Changes to the DataRepository.cst template.
\NetTiers\References\ - AzureSDKv1_0_200911\Microsoft.WindowsAzure.ServiceRuntime.dll - Copy this folder and DLL to your NetTiers\References folder. In the future we can add support for different versions of Windows Azure (currently there's only v1.0).
\NetTiers\VisualStudio\vsnet2005.project.cst - Changes to the vsnet2005.project.cst template. When "IsConnectionStringAzure" to "true", I add a reference to Microsoft.WindowsAzure.ServiceRuntime.dll so at runtime I can read the "netTiersConnectionString" from the Windows Azure configuration file ("ServiceConfiguration.cscfg").
Info on NetTiers-Support-Windows-Azure.zip (Azure SDK 2009/10 or earlier)
Here's what you'll find in the ZIP file:
\doc
01-Readme-NetTiers-Azure-Support-Code-Changes.txt - a log of the changes I made in the original (generated) C# code.
02-Readme-NetTiers-Azure-Support-Template-Changes.txt - a log of the changes I made to the .netTiers 2.3 templates. I also copy it below.\NetTiers.cst - changes to the main .netTiers template. Copy this file over your NetTiers.cst template.
\NetTiers\References\
AzureSDKv1_0\Microsoft.ServiceHosting.ServiceRuntime.dll - Copy this folder and DLL to your NetTiers\References folder. In the future we can add support for different versions of Windows Azure (currently there's only v1.0).\NetTiers\DataAccessLayer\DataRepository.cst - Changes to the DataRepository.cst template, copy this file over your DataAccessLayer\DataRepository.cst template.
\NetTiers\VisualStudio\vsnet2005.project.cst - Changes to the vsnet2005.project.cst template. When "IsConnectionStringAzure" to "true", I add a reference to Microsoft.ServiceHosting.ServiceRuntime.dll so at runtime I can read the "netTiersConnectionString" from the Windows Azure configuration file ("ServiceConfiguration.cscfg"). Copy this file over your VisualStudio\vsnet2005.project.cst template.
Detailed description of the changes that were made to the .netTiers templates (November 2009 or later)
1) Copied
C:\Program Files\Windows Azure SDK\v1.0\ref
Microsoft.WindowsAzure.ServiceRuntime.dll
Microsoft.WindowsAzure.ServiceRuntime..xml
to
\NetTiers\References\AzureSDKv1_0_200911
2) Edited NetTiers.cst:
After line 1303:
SafeCopyFile(this.CodeTemplateInfo.DirectoryName + "\\References\\nunit.framework.dll", libPath + "\\nunit.framework.dll");
Add:
SafeCopyFile(this.CodeTemplateInfo.DirectoryName + "\\References\\nunit.framework.dll", libPath + "\\nunit.framework.dll");
if(IsConnectionStringAzure)
{
SafeCopyFile(this.CodeTemplateInfo.DirectoryName + "\\References\\AzureSDKv1_0_200911\\Microsoft.WindowsAzure.ServiceRuntime.dll", libPath + "\\Microsoft.WindowsAzure.ServiceRuntime.dll");
}
3) Edited NetTiers\VisualStudio\vsnet2005.project.cst:
After line 258:
<ItemGroup>
Add:
<% if (IsConnectionStringAzure) { %>
<Reference Include="Microsoft.WindowsAzure.ServiceRuntime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\References\Microsoft.WindowsAzure.ServiceRuntime.dll</HintPath>
</Reference>
<%}%>
4) Edited NetTiers\DataAccessLayer\DataRepository.cst
After line 24:
using <%=DALNameSpace%>.Bases;
Add:
using <%=DALNameSpace%>.Bases;
<% if ( IsConnectionStringAzure ) { %>
using Microsoft.WindowsAzure.ServiceRuntime;
<% } %>
5) Edited NetTiers\DataAccessLayer\DataRepository.cst
Replace in line 299, collection ConnectionStrings:
public static ConnectionStringSettingsCollection ConnectionStrings
{
get
{
<% if ( IncludeDesignTimeSupport ) { %>
// use default ConnectionStrings if _section has already been discovered
if ( _config == null && _section != null )
{
return WebConfigurationManager.ConnectionStrings;
}
return Configuration.ConnectionStrings.ConnectionStrings;
<% } else { %>
return WebConfigurationManager.ConnectionStrings;
<% } // end if ( IncludeDesignTimeSupport ) { %>
}
}
With:
public static ConnectionStringSettingsCollection ConnectionStrings
{
get
{
<% if ( IsConnectionStringAzure ) { %>
ConnectionStringSettingsCollection oConnectionStrings = new ConnectionStringSettingsCollection();
ConnectionStringSettings oSettings = new ConnectionStringSettings();
oSettings.Name = "netTiersConnectionString";
if (RoleEnvironment.IsAvailable)
{
oSettings.ConnectionString = RoleEnvironment.GetConfigurationSettingValue("netTiersConnectionString");
oConnectionStrings.Add(oSettings);
return oConnectionStrings;
}
else
{
<% if ( IncludeDesignTimeSupport ) { %>
// use default ConnectionStrings if _section has already been discovered
if ( _config == null && _section != null )
{
return WebConfigurationManager.ConnectionStrings;
}
return Configuration.ConnectionStrings.ConnectionStrings;
<% } else { %>
return WebConfigurationManager.ConnectionStrings;
<% } // end if ( IncludeDesignTimeSupport ) { %>
}
<% } else { %>
<% if ( IncludeDesignTimeSupport ) { %>
// use default ConnectionStrings if _section has already been discovered
if ( _config == null && _section != null )
{
return WebConfigurationManager.ConnectionStrings;
}
return Configuration.ConnectionStrings.ConnectionStrings;
<% } else { %>
return WebConfigurationManager.ConnectionStrings;
<% } // end if ( IncludeDesignTimeSupport ) { %>
<% } // end if ( IsConnectionStringAzure ) { %>
}
}
Note that the 2009-11 Azure SDK allows us to check whether we are running inside Windows Azure (through "RoleEnvironment.IsAvailable") - we make this check to allow us to run our web roles inside the Azure local development fabric or directly in IIS (not in Azure).
Detailed description of the changes that were made to the .netTiers templates (October 2009 or earlier)
1) Copied
C:\Program Files\Windows Azure SDK\v1.0\ref
Microsoft.ServiceHosting.ServiceRuntime.dll
Microsoft.ServiceHosting.ServiceRuntime.xml
to
\NetTiers\References\AzureSDKv1_0
2) Edited NetTiers.cst:
Add under <%-- 2. Framework Generation Category --%>
<%@ Property Name="IsConnectionStringAzure" Type="System.Boolean" Default="False" Category="02. Framework Generation - Optional" Description="Indicates if at runtime the netTiersConnectionString is read from Window Azure's ServiceConfiguration.cscfg or from Web.config/app.config." %>
3) Edited NetTiers\DataAccessLayer\DataRepository.cst and add:
<%@ Property Name="IsConnectionStringAzure" Type="System.Boolean" Default="False" Category="02. Framework Generation - Optional" Description="Indicates if at runtime the netTiersConnectionString is read from Window Azure's ServiceConfiguration.cscfg or from Web.config/app.config." %>
4) Edited NetTiers\VisualStudio\vsnet2005.project.cst
<%@ Property Name="IsConnectionStringAzure" Type="System.Boolean" Default="False" Category="02. Framework Generation - Optional" Description="Indicates if at runtime the netTiersConnectionString is read from Window Azure's ServiceConfiguration.cscfg or from Web.config/app.config." %>
5) Edited NetTiers.cst:
After line 1904:
this.GetTemplate("DataRepository.cst").SetProperty("VisualStudioVersion", VisualStudioVersion);
Add:
this.GetTemplate("DataRepository.cst").SetProperty("VisualStudioVersion", VisualStudioVersion);
this.GetTemplate("DataRepository.cst").SetProperty("IsConnectionStringAzure", IsConnectionStringAzure);
6) Edit NetTiers.cst:
After line 2822:
this.GetTemplate(projectTemplate).SetProperty("ValidationType", ValidationType);
Add:
this.GetTemplate(projectTemplate).SetProperty("ValidationType", ValidationType);
this.GetTemplate(projectTemplate).SetProperty("IsConnectionStringAzure", IsConnectionStringAzure);
7) Edited NetTiers.cst:
After line 1303:
SafeCopyFile(this.CodeTemplateInfo.DirectoryName + "\\References\\nunit.framework.dll", libPath + "\\nunit.framework.dll");
Add:
SafeCopyFile(this.CodeTemplateInfo.DirectoryName + "\\References\\nunit.framework.dll", libPath + "\\nunit.framework.dll");
if(IsConnectionStringAzure)
{
SafeCopyFile(this.CodeTemplateInfo.DirectoryName + "\\References\\AzureSDKv1_0\\Microsoft.ServiceHosting.ServiceRuntime.dll", libPath + "\\Microsoft.ServiceHosting.ServiceRuntime.dll");
}
8) Edited NetTiers\VisualStudio\vsnet2005.project.cst:
After line 258:
<ItemGroup>
Add:
<% if (IsConnectionStringAzure) { %>
<Reference Include="Microsoft.ServiceHosting.ServiceRuntime, Version=0.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\References\Microsoft.ServiceHosting.ServiceRuntime.dll</HintPath>
</Reference>
<%}%>
9) Edited NetTiers\DataAccessLayer\DataRepository.cst
After line 24:
using <%=DALNameSpace%>.Bases;
Add:
using <%=DALNameSpace%>.Bases;
<% if ( IsConnectionStringAzure ) { %>
using Microsoft.ServiceHosting.ServiceRuntime;
<% } %>
10) Edited NetTiers\DataAccessLayer\DataRepository.cst
Replace in line 299, collection ConnectionStrings:
public static ConnectionStringSettingsCollection ConnectionStrings
{
get
{
<% if ( IncludeDesignTimeSupport ) { %>
// use default ConnectionStrings if _section has already been discovered
if ( _config == null && _section != null )
{
return WebConfigurationManager.ConnectionStrings;
}
return Configuration.ConnectionStrings.ConnectionStrings;
<% } else { %>
return WebConfigurationManager.ConnectionStrings;
<% } // end if ( IncludeDesignTimeSupport ) { %>
}
}
With:
public static ConnectionStringSettingsCollection ConnectionStrings
{
get
{
<% if ( IsConnectionStringAzure ) { %>
ConnectionStringSettingsCollection oConnectionStrings = new ConnectionStringSettingsCollection();
ConnectionStringSettings oSettings = new ConnectionStringSettings();
oSettings.Name = "netTiersConnectionString";
oSettings.ConnectionString = RoleManager.GetConfigurationSetting("netTiersConnectionString");
oConnectionStrings.Add(oSettings);
return oConnectionStrings;
<% } else { %>
<% if ( IncludeDesignTimeSupport ) { %>
// use default ConnectionStrings if _section has already been discovered
if ( _config == null && _section != null )
{
return WebConfigurationManager.ConnectionStrings;
}
return Configuration.ConnectionStrings.ConnectionStrings;
<% } else { %>
return WebConfigurationManager.ConnectionStrings;
<% } // end if ( IncludeDesignTimeSupport ) { %>
<% } // end if ( IsConnectionStringAzure ) { %>
}
}
Good times!
Comments