Para todos los que hemos trabajamos con Crystal Reports, no es un secreto que cuando tratamos de conectar nuestro reporte directamente a la base de datos, se nos viene encima el problema de autenticación. Es decir nuestro reporte al momento de iniciar la carga nos solicita autentificarnos en el servidor y sino lo hacemos, simplemente no veremos el reporte. Esto, además de ser tedioso para los usuarios se convierte en un problema de seguridad bastante grande, de ahí que en la mayoría de los casos se recomienda utilizar dataset. Sin embargo, para todos los que aún sabiendo esto no desean utilizar datasets, sino que, quieren conectar su crystal directamente veremos como implementar una pequeña clase que nos ayudará con esa tarea. Generalmente, cuando trabajamos con una aplicación web, nuestra cadena de conexión esta incluida en el web.config y también en muchas ocasiones contiene los datos como el usuario y password para acceder a la base de datos. De esta cadena de conexión y estos datos es de los que nos ayudaremos para implementar la autentificación en el reporte. Generalmente, la cadena de conexión se vería así <connectionStrings>
<remove name="LocalSqlServer"/>
<add name="xxx" connectionString="Data Source=.\SqlExpress;Integrated Security=False;Initial Catalog=xxx;user id=myuser;password=mypass" providerName="System.Data.SqlClient"/>
</connectionStrings>
Para nuestro ejemplo, nombraremos a nuestra clase CrystalRules (es solo algo que pensé de momento)
1. Primer Paso
Creamos una variable de tipo SqlConnectionStringBuilder, a la cual le asignaremos la cadena de conexión que definimos en el web.config, y que luego utilizaremos para obtener los datos del usuario y el password para el crystal report.
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(ConfigurationManager.ConnectionStrings["xxx"].ConnectionString);
2. Implementación de propiedad
Para ser más ordenados crearemos varias propiedad de tipo Privado, que se encargarán de recibir los datos de:
La Base de datos, el password, el usuario y el servidor
private string _dbName;
private string _serverName;
private string _userID;
private string _passWord;
private string dataBase
{
get { return _dbName; }
set { _dbName = value; }
}
private string serverName
{
get { return _serverName; }
set { _serverName = value; }
}
private string userName
{
get { return _userID; }
set { _userID = value; }
}
private string dataBasePassword
{
get { return _passWord; }
set { _passWord = value; }
}
3. Creación del Método para aplicar los datos de conexión
Una vez que ya tenemos las propiedades, asignaremos a las variables los valores que se han recogido en el SqlConnectionStringBuilder. Y crearemos una variable de tipo ConnectionInfo para aplicar los datos de conexión.
internal void ApplyInfo(ReportDocument _oRpt)
{
dataBase = builder.InitialCatalog;
serverName = builder.DataSource;
userName = builder.UserID;
dataBasePassword = builder.Password;
Database oCRDb = _oRpt.Database;
Tables oCRTables = oCRDb.Tables;
//Table oCRTable = default(Table);
TableLogOnInfo oCRTableLogonInfo = default(TableLogOnInfo);
ConnectionInfo oCRConnectionInfo = new ConnectionInfo();
oCRConnectionInfo.DatabaseName = _dbName;
oCRConnectionInfo.ServerName = _serverName;
oCRConnectionInfo.UserID = _userID;
oCRConnectionInfo.Password = _passWord;
foreach (Table oCRTable in oCRTables)
{
oCRTableLogonInfo = oCRTable.LogOnInfo;
oCRTableLogonInfo.ConnectionInfo = oCRConnectionInfo;
oCRTable.ApplyLogOnInfo(oCRTableLogonInfo);
}
}
4. Creación del report document y aplicación de la seguridad
Una vez recogidos los datos y asignados, crearemos un elemento report document al cual le asignaremos el CrystalReportViewer y le aplicaremos los datos de acceso que obtuvimos anteriormente
public void loadReport(string repName, CrystalReportViewer viewer)
{
// attached our report to viewer and set database login.
ReportDocument report = new ReportDocument();
report.Load(HttpContext.Current.Server.MapPath("~/Reports/" + repName));
ApplyInfo(report);
viewer.ReportSource = report;
}
Al final, nuestra clase completa ser vería así
public class CrystalRules
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(ConfigurationManager.ConnectionStrings["Fatchoy.Data.Properties.Settings.FatchoyConnectionString"].ConnectionString);
private string _dbName;
private string _serverName;
private string _userID;
private string _passWord;
private string dataBase
{
get { return _dbName; }
set { _dbName = value; }
}
private string serverName
{
get { return _serverName; }
set { _serverName = value; }
}
private string userName
{
get { return _userID; }
set { _userID = value; }
}
private string dataBasePassword
{
get { return _passWord; }
set { _passWord = value; }
}
internal void ApplyInfo(ReportDocument _oRpt)
{
dataBase = builder.InitialCatalog;
serverName = builder.DataSource;
userName = builder.UserID;
dataBasePassword = builder.Password;
Database oCRDb = _oRpt.Database;
Tables oCRTables = oCRDb.Tables;
//Table oCRTable = default(Table);
TableLogOnInfo oCRTableLogonInfo = default(TableLogOnInfo);
ConnectionInfo oCRConnectionInfo = new ConnectionInfo();
oCRConnectionInfo.DatabaseName = _dbName;
oCRConnectionInfo.ServerName = _serverName;
oCRConnectionInfo.UserID = _userID;
oCRConnectionInfo.Password = _passWord;
foreach (Table oCRTable in oCRTables)
{
oCRTableLogonInfo = oCRTable.LogOnInfo;
oCRTableLogonInfo.ConnectionInfo = oCRConnectionInfo;
oCRTable.ApplyLogOnInfo(oCRTableLogonInfo);
}
}
public void loadReport(string repName, CrystalReportViewer viewer)
{
// attached our report to viewer and set database login.
ReportDocument report = new ReportDocument();
report.Load(HttpContext.Current.Server.MapPath("~/Reports/" + repName));
ApplyInfo(report);
viewer.ReportSource = report;
}
#region instance
private static CrystalRules m_instance;
// Properties
public static CrystalRules Instance
{
get
{
if (m_instance == null)
{
m_instance = new CrystalRules();
}
return m_instance;
}
}
public DataDataContext m_DataContext
{
get
{
return DataDataContext.Instance;
}
}
#endregion instance
}
Si bien, la solución no es robusta y no es la mas segura. En casos de uso como una intranet y cuando estamos contra tiempo, podría ser de gran ayuda.