We have several environments:
Development
Azure
On Prem
The Crystal Report pulls data from a SQL server, but the database is different depending on the environment.
There is a config file in the project called connectionStrings.config and the connection string that we use for the website is stored there. The contents of the file looks like this:
<connectionStrings>
<add name="dataModel" connectionString="data source=...;initial catalog=...;user id=...;password=...;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
To load the report I setup the following class:
public class CrystalReportUtil
{
static string ReportsDirectory = ConfigurationManager.AppSettings["ReportsDirectory"];
private static string ConnectionString = ConfigurationManager.ConnectionStrings["dataModel"].ConnectionString;
public static ReportDocument GetReport(string reportName)
{
var report = new ReportDocument();
var fileDirectory = $"{Utilities.GetProjectRoot()}/Reports";
if (System.Web.HttpContext.Current != null)
{
fileDirectory = System.Web.HttpContext.Current.Server.MapPath(ReportsDirectory);
}
string file = Path.Combine(fileDirectory, $"{reportName}.rpt");
if (!File.Exists(file))
{
throw new System.Exception($"Unable to find report file: {file}");
}
report.Load(file);
var builder = new SqlConnectionStringBuilder(ConnectionString);
var dataSource = builder.DataSource;
var user = builder.UserID;
var password = builder.Password;
var database = builder.InitialCatalog;
RecursivelyRemapConnection(report, user, password, dataSource, database);
report.VerifyDatabase();
return report;
}
private static void RecursivelyRemapConnection(ReportDocument report, string username, string password, string server, string database)
{
foreach (IConnectionInfo connection in report.DataSourceConnections)
{
connection.SetLogon(username, password);
connection.SetConnection(server, database, false);
}
report.SetDatabaseLogon(username, password);
foreach (Table table in report.Database.Tables)
{
table.LogOnInfo.ConnectionInfo.ServerName = server;
table.LogOnInfo.ConnectionInfo.DatabaseName = database;
table.LogOnInfo.ConnectionInfo.UserID = username;
table.LogOnInfo.ConnectionInfo.Password = password;
table.LogOnInfo.ConnectionInfo.IntegratedSecurity = false;
}
if (!report.IsSubreport)
{
foreach (ReportDocument subreport in report.Subreports)
{
RecursivelyRemapConnection(subreport, username, password, server, database);
}
}
}
}
The issue is that in my development environment, it is working just fine whereas in the other environments I get the following exception:
Log on failed. No error.
at CrystalDecisions.ReportAppServer.ClientDoc.ReportClientDocumentClass.VerifyDatabase()
at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.VerifyDatabase()
at CrystalDecisions.CrystalReports.Engine.ReportDocument.VerifyDatabase()
I verified that the connection is changing in my development environment by doing the following:
Creating a copy of my datasource
In the designer, setting the datasource to the copied database
Deleting the copy
Previewing the report in the designer fails to load (as expected)
Generating the report using my utility class
The last step successfully generates the report which tells me that the datasource is changing, otherwise I would expect a logon failure.
What makes this particularly frustrating is that some reports in the non-developing environments work fine whereas some are producing the exception. So I don't understand why it works for some, but not for all.
Update
If I change my connection string in my development environment to the connection string used in Azure or in the On-Prem, it still works in my development environment. I really don't believe that the issue is with my connection string, but then again I have no idea what is causing the logon failure at this point.
After several days of looking into this, I finally found out what was causing the issue.
The particular report that was failing was using the MSOLEDBSQL provider instead of SQLOLEDB.
To fix the issue, I:
Opened the Crystal Report in Visual Studio
Right-clicked on the report
Database > Set Datasource Location
Expanded the properties of the current datasource
Double-clicked on the provider
Changed the value from MSOLEDBSQL to SQLOLEDB
Reentered the logon credentials (even though they get changed at runtime)
Saved the report
Build the solution
Republished the project
Related
I am kind of new to this environment. What we want to do is to have the end user login with their Login name that is stored in the Security/Logins section. I am not sure where I can go to add this:
SELECT * FROM sys.sysusers WHERE name = variablename
any idea on where to go?
We are using Visual Studio 2013 / Framework 4.6 / SQL Server 2005
To log in with the SQL accounts you must build the provided username and password into the database connection string. If the user is able to successfully open an SqlConnection object, they can log in.
You'll have a saved connection string that looks something like this:
Server=MyServer;Database=MyDatabase;User ID={0};Password={1};
When the user tries to log in, you do something like this:
//pull this from web.config or similar
private string LoginCnString = "Server=MyServer;Database=MyDatabase;User ID={0};Password={1};";
public bool Login(string Username, string Password)
{
using (var cn = new SqlConnection(string.Format(LoginCnString, Username, Password)))
{
try
{
cn.Open();
//Login succeeds
return true;
}
catch
{
//Login fails
return false;
}
}
}
You might also have a generic connection string you use for more of the mundane work of the application, but this is how you validate the login.
I am having a C# API that takes an Excel sheet (filePath, sheetName) as an input and return the sheet content as the output in JSON form.
The API works fine when I try to test it on my machine that contains windows server 2016 installed on it. But it always fail when I try to send the file path and sheet name from the form.
This is my API Code...
public IHttpActionResult GetSheetData(string filePath, string sheetName)
{
try
{
var connectionString = $#"
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source={filePath};
Extended Properties=""Excel 12.0 Xml;HDR=YES""";
using (var conn = new OleDbConnection(connectionString))
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = $#"SELECT * FROM [{sheetName}$]";
using (var DRow = cmd.ExecuteReader())
{
var query = DRow.Cast<DbDataRecord>().Select(row => new
{
name = row[0],
age = row[1],
email = row[2]
});
var JSON = JsonConvert.SerializeObject(query);
return Ok(JSON);
}
}
}
catch (Exception) { return Ok(HttpStatusCode.NotFound); };
}
The JSON result is returned perfectly when I test the API on the Server, But when I try to test it using this form..
It always returns 404 (Not Found).
This is my sample excel sheet data.
Any Ideas?!
I have found the solution to my Issue, and I wanted to share with you.
The Issue was all about the access rights of the IIS.
When I try to access a file on my local machine or in the clients machine, the IIS App Pool must have an access rights on that location.
So, I used the following steps.
Browse to the folder containing the file -to be read-
Right Click the folder and select 'Properties'
Under 'Security' Tab, select Edit.
In the 'Edit' Window select 'Add' -> 'Advanced' -> Find Now
Look up the result names while you find the user related to IIS -[IIS-IUsers]- in my case.
allow access controls to the folder -full access- or -Read and Write- etc...
I Hope this is helpful for you.
Thank you all.
For the past few hours I've been looking into this problem, found many similar topics but none of them seems to help.
I have a C# application that uses the Entity Framework 5 and I have a .edmx data model that works fine when using the connection string from the app config file.
Now I would like to change the connection string during runtime, but it throws an exception that says:
Keyword not supported: 'metadata'.
Here is the code that I have:
private string GetNewConnectionString(string server, string database, string username, string password)
{
var sqlBuilder = new SqlConnectionStringBuilder()
{
DataSource = server,
InitialCatalog = database,
UserID = username,
Password = password,
IntegratedSecurity = true,
MultipleActiveResultSets = true
};
var entityBuilder = new EntityConnectionStringBuilder()
{
Provider = "System.Data.SqlClient",
ProviderConnectionString = sqlBuilder.ToString(),
Metadata = "res://*/MyTestModel.MyTestModel.csdl|res://*/MyTestModel.MyTestModel.ssdl|res://*/MyTestModel.MyTestModel.msl"
};
return entityBuilder.ToString();
}
public void insertInDB()
{
var newConnectionString = GetNewConnectionString(server, database, username, password);
//newConnectionString = newConnectionString .Replace("\"", """); // doesn't help
//newConnectionString = newConnectionString .Replace("\"", "'"); // doesn't help either
using (var context = new MyTestModel.MyEntities(newConnectionString)) // it crashes here
{
}
}
The metadata should be correct, because I have copied it from the app config file, also if I copy the value of newConnectionString to the app config and use it, it works fine if I replace the quotations with ".
This is the value of newConnectionString (i've only replaced the credentials with some dummy credentials):
metadata=res:///MyTestModel.MyTestModel.csdl|res:///MyTestModel.MyTestModel.ssdl|res://*/MyTestModel.MyTestModel.msl;provider=System.Data.SqlClient;provider
connection string="Data Source=myServer;Initial
Catalog=myDatabase;Integrated Security=True;User
ID=myDbUser;Password=myDbUserPassword;MultipleActiveResultSets=True"
I cannot see what is wrong with it, can anybody spot something?
This answer: Entity Framework change connection at runtime solves the problem
I cannot understand what the problem with my code was, but it works fine with the extension method from the link.
I have a database in Analysis Services on a remote server. This contains a data source for another database located on another remote server.
I am trying to write a connectivity test using C# which will check the database connection between the two databases.
I have been unable to do this using ADOMD.NET. I'm currently looking at using SMO to do this but I haven't had any luck so far.
I would greatly appreciate any advice or suggestions.
Update:
After further research, I have come up with the below test (Please note that I intend to add more try..catch blocks and Assertions later).
Also, this uses C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.AnalysisServices.DLL to access the Server, Database and
DataSource classes.
class ConnectivityTests
{
// Variables
String serverName = "";
String databaseName = "";
String dataSourceName = "";
[Test]
public void TestDataSourceConnection()
{
// Creates an instance of the Server
Server server = new Server();
server.Connect(serverName);
// Gets the Database from the Server
Database database = server.Databases[databaseName];
// Get the DataSource from the Database
DataSource dataSource = database.DataSources.FindByName(dataSourceName);
// Attempt to open a connection to the dataSource. Fail test if unsuccessful
OleDbConnection connection = new OleDbConnection(dataSource.ConnectionString);
try
{
connection.Open();
}
catch (OleDbException e)
{
Assert.Fail(e.ToString());
}
finally
{
connection.Close();
}
}
I believe that this test is sufficient for my testing (Once I've added some more try..catch blocks and Assertions). If the test passes, it means there are no connectivity issues between my machine and both servers, which implies that there shouldn't be any connectivity issues between the servers.
However, I have been unable to work out how to test the connection between the two servers directly and I am interested if anyone knows a way of doing this.
The best solution I have come across to doing this connectivity test is below:
Please note that this requires the Microsoft.AnalysisServices.DLL to be added as a reference.
class ConnectivityTests
{
// Variables
String serverName = "";
String databaseName = "";
String dataSourceName = "";
[Test]
public void TestDataSourceConnection()
{
try
{
// Creates an instance of the Server
Server server = new Server();
server.Connect(serverName);
// Gets the Database from the Server
Database database = server.Databases[databaseName];
// Get the DataSource from the Database
DataSource dataSource = database.DataSources.FindByName(dataSourceName);
// Attempt to open a connection to the dataSource. Fail test if unsuccessful
OleDbConnection connection = new OleDbConnection(dataSource.ConnectionString);
connection.Open();
}
catch (Exception e)
{
Assert.Fail(e.ToString());
}
finally
{
connection.Close();
}
}
}
I’m currently working in c# with a SQLite database file (data.db3) which is located in the application directory. During development, an absolute path has been used and it worked fine so far. Now I’m trying to access the database by using a relative path, but that fails because of a possibly wrong connection string. The following connection string works fine and was automatically created by the ADO.Net framework.
<connectionStrings>
<add name="dataEntities"
connectionString="metadata=res://*/DataEntities.csdl|res://*/DataEntities.ssdl|res://*/DataEntities.msl;provider=System.Data.SQLite;provider connection string='data source="C:\Projekte\DataProvider\data.db3";datetimeformat=Ticks'"
providerName="System.Data.EntityClient" />
</connectionStrings>
Now I tried the following to access the database using a relative path (all fails):
dataContext = new dataEntities("Data Source=data.db3");
dataContext = new dataEntities("Data Source=.\\data.db3");
dataContext = new dataEntities("Data Source=data.db3;Version=3;DateTimeFormat=Ticks;");
dataContext = new dataEntities("metadata=res://*/DataEntities.csdl|res://*/DataEntities.ssdl|res://*/DataEntities.msl;provider=System.Data.SQLite;provider connection string='data source="data.db3";datetimeformat=Ticks'" providerName="System.Data.EntityClient");
Created by the ADO.Net framework:
public partial class dataEntities : ObjectContext
{
public dataEntities() : base("name=dataEntities", "dataEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
public dataEntities(string connectionString) : base(connectionString, "dataEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// ……
}
You might want to consider using the EntityConnectionStringBuilder class
Which will simplify at least isolating the connection string down to just the SQL part.
string baseFolder = AppDomain.CurrentDomain.BaseDirectory;
string sqlLiteConnectionString = string.Format(
"data source=\"{0}\";datetimeformat=Ticks",
Path.Combine(baseFolder, "data.db3"));
var entityConnectionString = new EntityConnectionStringBuilder
{
Metadata = "res://*",
Provider = "System.Data.EntityClient",
ProviderConnectionString = sqlLiteConnectionString,
}.ConnectionString;
var entities = new dataEntities(entityConnectionString);
The problem is NOT relative versus absolute path but the fact that you are trying to write in the application directory which is prohibited by newer Windows version for security reasons...
So this more an issue of permission/rights - depending on your OS (i.e. Windows 7...) and the user your running the app with (i.a. Administrator?) for security reasons you are not allowed to write in the application directory... if you need someplace with read+write you should use http://msdn.microsoft.com/de-de/library/system.windows.forms.application.userappdatapath.aspx
Just check whether the db is in that path -if not copy it there- and use it there...
Other locations could be ApplicationData/CommonApplicationData/LocalApplicationData from http://msdn.microsoft.com/de-de/library/14tx8hby.aspx and http://msdn.microsoft.com/de-de/library/system.environment.specialfolder.aspx