Set Read-Only Access for Crystal Report in AlwaysOn environment - c#

I have two SQL servers that are being load balanced - AlwaysOn. Only the second one of these servers is supposed to be used for crystal reports. I would like to access the second SQL server using the readOnly flag in the connection string: ApplicationIntent=ReadOnly
In my C# class I am running the crystal reports based on ConnectionInfo()
var myConnectionInfo = new ConnectionInfo();
Tables myTables = reportDocument.Database.Tables;
for (int i = 0; i < myTables.Count; i++)
{
var myTable = myTables[i];
var myTableLogonInfo = myTable.LogOnInfo;
myConnectionInfo.ServerName = 'serverName';
myConnectionInfo.DatabaseName = 'databaseName';
myConnectionInfo.UserID = 'userId';
myConnectionInfo.Password = 'password';
myTableLogonInfo.ConnectionInfo = myConnectionInfo;
myTable.ApplyLogOnInfo(myTableLogonInfo);
}
I haven't found a way to set ApplicationIntent=ReadOnly though. Is this supposed to be done setting myConnectionInfo.Attributes? Unfortunately I haven't found an answer on this yet but unanswered questions:
https://archive.sap.com/discussions/thread/3861287
https://archive.sap.com/discussions/thread/3791155

Unfortunately I did not find a way to use the flag ApplicationIntent=ReadOnly in my posted code snippet.
What I ended up doing:
Instead of using the load balancer IP address (or host name), I am using the IP address of the reporting server directly.
I couldn't find any written documentation whether or not one can use ApplicationIntent=ReadOnly.

Rather than using ConnectionInfo, you could use System.Data.SqlClient.SqlConnectionStringBuilder, which has a settable ApplicationIntent property.

Related

SQL Server Reporting Services Cannot Set Data Source Connection String

We have a number of SSRS sites serving reports to various locations. Each of these servers all have custom connections in each and every report (don't ask why, that's a tale too torrid to tell). Our goal is to replace all of these custom data sources with a single shared data source for all reports on each server.
To that end, I have created a C# program that will find each report on each server and point the current custom data sources to a currently existing shared data source. This executes and seems to work fine.
My next goal is to use C# to create the shared data source on each server where none currently exists.
My current dilemma arises here:
private static void CreateSharedDataSource(string user, string password, string connection)
{
DataSourceDefinition source = new DataSourceDefinition();
source.CredentialRetrieval = CredentialRetrievalEnum.Store;
source.ConnectString = connection;
source.Enabled = true;
source.EnabledSpecified = true;
source.Extension = "SQL";
source.ImpersonateUser = false;
source.ImpersonateUserSpecified = false;
source.Prompt = "Enter a user name and password to access the data source:";
source.UserName = user;
source.Password = password;
source.WindowsCredentials = true;
source.OriginalConnectStringExpressionBased = true;
source.UseOriginalConnectString = true;
try
{
service.CreateDataSource("DbDataSource", "/DsFolder", false, source, null);
Console.WriteLine("Data source created successfully");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
The data source is created correctly and the user name and password are updated correctly. The problem is that, when I look at the newly created data source on the server, the Connect string property is empty and, yes, the correct value is being passed to it in the method above. If I plug that value into the shared source on the server and test the connection, it works fine, but I cannot get the C# program to update that value itself.
So, is there something subtle I'm missing? Am I misinterpreting a setting up there? Did I set a value wrong?
Clues appreciated.
I've never tried anything like this but a quick bit of research uncovered this which may be helpful.
https://learn.microsoft.com/en-us/dotnet/api/reportservice2010.datasourcedefinition.originalconnectstringexpressionbased?view=sqlserver-2016
It states
"Expression-based connection strings are not supported in shared data
sources."
Assuming the conneciton string is just a plain text string then I would guess that you could set this to false. This may help preserve the string you pass in.
Alright, found my answer. I had a pre-existing data source that was working so, instead of creating it from scratch, I copied that and only changed the name. That created a data source where the Connect string did persist. Comparing the settings in that with what I was setting revealed:
source.UseOriginalConnectString = false;
whereas, my code was:
source.UseOriginalConnectString = true;
Looking that up in docs and it tells me "If true, the value of the ConnectString property is ignored."
Hmmm... that's intuitive. That's not what that sounds like at all. :)

c# using a binding source to add row to an sql table with incremental identity field

I have a program I created in Visual studio. The program is basically a place for everyone to store passwords for company and external accounts. I want to further this application by automatically creating the company accounts when I create a new user. I approached this by using the binding source. I can get the row into the database but it doesn't use the sql supplied auto increment. I will post the code but I am trying to figure out if I went about this the wrong way. I am not 100% familiar with how the connector and classes that visual studio create when you connect the solution to the database. I am not looking for code to help me do this I am looking for explanations and guidance. If responding with code please help me understand by explaining the code.
DataSet.AccountsRow newdomainuserrow = DBDataSet.Accounts.NewAccountsRow();
newdomainuserrow.USer = userIDTextBox.Text.ToString();
newdomainuserrow.UserName = userIDTextBox.Text.ToString();
System.DateTime moment = new DateTime();
newdomainuserrow.Password = moment.Year;
newdomainuserrow.AccountName = "Domain";
drawingNumDBDataSet.Accounts.Rows.Add(newdomainuserrow);
MessageBox.Show("User Saved");
this.Validate();
this.usersBindingSource.EndEdit();
this.accountBindingSource.Endedit();
this.tableAdapterManager.UpdateAll(this.DataSet);
All help is greatly appreciated.
Matt
I found a solution. The id field is not longer an identity autoincrement field. To increment the id field one by one programmatically like I need to I wrote a simply while statement to get all numbers that were not used. This works if there is a deleted row it will insert one where there is one missing. here is the code I used.
Boolean gotnum;
gotnum = false;
int idnum = 1;
while (gotnum != true)
{
DrawingNumDBDataSet.AccountsRow actrw = drawingNumDBDataSet.Accounts.FindById(idnum);
idnum++;
if (actrw==null)
{
gotnum = true;
idnum--;
}
}
I then set the Id field = to idnum. This is probably not the best practice but it is the best I could come up with.

SSRS Set "Credentials stored securely in the report server" programmatically

I am trying to set a given RDL report to use an embedded data source using my client application. I am using the ReportingService2005 class to interact with SSRS. I need to set the embedded data source to use "Credentials stored securely in the report server" and specify the username and password.
Thank you!
I solved the issue by first publishing the RDL, then calling into the ReportingService2005 GetItemDataSources() method. I then modified that datasource and subsequently called SetItemDataSources() to save the changes into SSRS. Below is a snippet of the code I accomplished this with:
var reportItem = report.TargetFolder + "/" + report.Name;
var dataSources = new DataSource[0];
dataSources = rs.GetItemDataSources(reportItem);
if (dataSources.Any())
{
var dataSource = (DataSourceDefinition)dataSources.First().Item;
dataSource.CredentialRetrieval = CredentialRetrievalEnum.Store;
dataSource.UserName = SsrsUsername;
dataSource.Password = SsrsPassword;
rs.SetItemDataSources(reportItem, dataSources);
}

How to Connect to SQL Server Programmatically in C#?

I have developed a program (C#) which creates a SQL database using this code:
string SQLCreation = "IF NOT EXISTS (SELECT * FROM master..sysdatabases WHERE Name = 'x') CREATE DATABASE x";
SqlConnection PublicSQLDBCreationConnection = new SqlConnection(connectionString);
SqlCommand PublicSQLDBCreation = new SqlCommand(SQLCreation, PublicSQLDBCreationConnection);
try
{
PublicSQLDBCreationConnection.Open();
PublicSQLDBCreation.ExecuteNonQuery();
PublicSQLDBCreationConnection.Close();
}
//'then creates a table and so on
Now I want to have a client application which connects to this database (via LAN) WITHOUT using IP or computer name. How is that possible? Is it possible do this and have a dataset while not mentioning IP Adr. or computer name?
P.S. Don't Worry Guys, I simplified my code just for your view, I have made sure that SQL injection or other attempts won't happen.
Also I have to say that My Reason for not mentioning servername or IP is that I want to mass deploy my Application on many Networks
You could use SqlDataSourceEnumerator to get a list of all Sql Servers that are visible and browsable. This is not a good technique, since you could get an instance that you don't have the right to create a database on it, but you could still try something with that.
var enumerator = SqlDataSourceEnumerator.Instance;
foreach (DataRow row in enumerator.GetDataSources().Rows)
{
var serverName = row["ServerName"];
var instance = row["InstanceName"];
// build a connection string and try to connect to it
}

SQL Server SMO TransferData() runs slowly

I'm following http://www.mssqltips.com/tip.asp?tip=1910 and the following code actually does work, but takes about 90 seconds to run. The database schema has less than 10 (fairly straightforward) tables. I'm not sure why it takes so long. Any suggestions on how to debug this?
var host = "192.168...";
var user = "username";
var pass = "password";
var srcDbName = "srcDbName";
var dstDbName = "dstDbName";
var server = new Server(new ServerConnection(host, user, pass));
var srcDb = server.Databases[srcDbName];
var dstDb = new Database(server, dstDbName);
dstDb.Create();
var transfer = new Transfer(srcDb);
transfer.CopyAllTables = true;
transfer.Options.DriAll = true;
transfer.Options.ContinueScriptingOnError = false;
transfer.DestinationDatabase = dstDbName;
transfer.DestinationServer = server.Name;
transfer.DestinationLoginSecure = false;
transfer.DestinationLogin = user;
transfer.DestinationPassword = pass;
transfer.TransferData();
I think that you should leave this code alone. You cannot improve it. AS I understand from the question you want to transfer tables/schema from one database to other .
Below options that I suggest :
Linked Servers
From SQL 2000 you should be able to connect directly to other database as a linked server. In the pros column this kind of direct access can be easy to work with if you don't have any other technical skills such as DTS or SSIS, but it can be complex to get the initial set-up right and there may be security concerns/issues.
DTS
DTS is packaged with SQL 2000 and is made for this kind of a task. If written correctly, your DTS package can have good error-handling and be rerunnable/reusable.
SSIS
SSIS is actually packaged with SQL 2005 and above, but you can connect it to other databases. It's basically a better version of DTS.

Categories