M
Mike
Hi. I'm trying to build a simple ASP.net-based code generation tool
for my company's intranet using T4 text templates. The templates will
accept as inputs a connection string and the name of a database table,
and depending on the template will output various code elements based
on the table definition, such as stored procedures, data access
methods, business classes, etc. The user will have the option of
establishing the database connection using either SQL or Windows
authentication, and will be prompted to provide the appropriate
credentials (SQL/Windows username and password). I'm creating a
System.Diagnostics.Process object to launch TextTransform.exe to
process the templates. Everything works fine when using SQL
authentication, but I'm having problems when trying to use Windows
authentication to make the connection to the database.
First I tried impersonation using WindowsIdentity.Impersonate()
immediately before launching TextTransform.exe:
I know the impersonation is kicking in correctly because I can see it
in my log entry. But the template doesn't get processed, and the
standard error stream contains only the message "Error: The type
initializer for 'Microsoft.VisualStudio.TextTemplating.Engine' threw
an exception." I haven't been able to get anything more specific.
So then instead of impersonation I tried passing in the credentials
through the process.StartInfo:
But then the TextTransform.exe process hangs indefinitely; I have to
kill it in the task manager.
Here is a portion of my template if it helps, but I don't think the
template itself is the problem since it works under SQL
Authentication:
Thanks,
MJ
for my company's intranet using T4 text templates. The templates will
accept as inputs a connection string and the name of a database table,
and depending on the template will output various code elements based
on the table definition, such as stored procedures, data access
methods, business classes, etc. The user will have the option of
establishing the database connection using either SQL or Windows
authentication, and will be prompted to provide the appropriate
credentials (SQL/Windows username and password). I'm creating a
System.Diagnostics.Process object to launch TextTransform.exe to
process the templates. Everything works fine when using SQL
authentication, but I'm having problems when trying to use Windows
authentication to make the connection to the database.
First I tried impersonation using WindowsIdentity.Impersonate()
immediately before launching TextTransform.exe:
Code:
IntPtr userToken = IntPtr.Zero;
if (NativeMethods.LogonUser(userName, domain, password,
NativeMethods.LOGON32_LOGON_INTERACTIVE,
NativeMethods.LOGON32_PROVIDER_DEFAULT, ref userToken) != 0)
{
IntPtr userTokenDuplicate = IntPtr.Zero;
if (NativeMethods.DuplicateToken(userToken,
NativeMethods.LOGON32_LOGON_INTERACTIVE, ref userTokenDuplicate) != 0)
{
using (WindowsIdentity identity = new WindowsIdentity
(userTokenDuplicate))
{
using (WindowsImpersonationContext context = identity.Impersonate
())
{
Log("Current user: {0}", Thread.CurrentPrincipal.Identity.Name);
using (Process process = new Process())
{
process.StartInfo.FileName = Server.MapPath("TextTransform.exe");
process.StartInfo.Arguments = sbArguments.ToString(); //
StringBuilder filled above
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
Log("Launching \"{0}\" {1}", process.StartInfo.FileName,
process.StartInfo.Arguments);
process.Start();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
process.Close();
if (error.Length > 0)
{
Log(error);
}
}
context.Undo();
}
}
}
}
return File.ReadAllText(outputFile);
I know the impersonation is kicking in correctly because I can see it
in my log entry. But the template doesn't get processed, and the
standard error stream contains only the message "Error: The type
initializer for 'Microsoft.VisualStudio.TextTemplating.Engine' threw
an exception." I haven't been able to get anything more specific.
So then instead of impersonation I tried passing in the credentials
through the process.StartInfo:
Code:
process.StartInfo.Domain = domain;
process.StartInfo.UserName = userName;
process.StartInfo.Password = new SecureString();
foreach (char c in password.ToCharArray())
{
process.StartInfo.Password.AppendChar(c);
}
But then the TextTransform.exe process hangs indefinitely; I have to
kill it in the task manager.
Here is a portion of my template if it helps, but I don't think the
template itself is the problem since it works under SQL
Authentication:
Code:
<#@ template language="C#" hostspecific="true" #>
<#@ output extension=".sql" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="Microsoft.SqlServer.Management.Common" #>
<#@ import namespace="Microsoft.SqlServer.Management.Smo" #>
<#
string connectionString = Host.ResolveParameterValue(null, null,
"ConnectionString");
string tableName = Host.ResolveParameterValue(null, null,
"TableName");
if (connectionString != null && tableName != null)
{
SqlConnectionStringBuilder connStringBuilder = new
SqlConnectionStringBuilder(connectionString);
using (SqlConnection sqlConnection = new SqlConnection
(connStringBuilder.ConnectionString))
{
ServerConnection serverConnection = new ServerConnection
(sqlConnection);
Server server = new Server(serverConnection);
Database database = server.Databases
[connStringBuilder.InitialCatalog];
Table table = database.Tables[tableName];
table.Refresh();
string storedProcedureName = string.Format("PROC_{0}_Get",
tableName);
#>
-- connected as <#= serverConnection.TrueLogin #>
IF EXISTS (SELECT * FROM sys.procedures WHERE Name = '<#=
storedProcedureName #>')
BEGIN
DROP PROCEDURE <#= table.Schema #>.<#= storedProcedureName #>
END
GO
-- etc . . .
<#
}
}
#>
Thanks,
MJ