I have recently placed a web application on a remote server for testing. This application uses a range of SQL Server (Express) databases to hold card and user information, of which all are linked into my master database. The database is referred to in my web.config file.
I have made .bak files from my databases and restored them on my server, with the connection string now showing:
<add key="ConnectionString" value="server=localhost; database=DatabaseMaster; uid=...; pwd=..."/>
On my local computer the application is fine and throws no exceptions. However, upon connecting to my application via the web server, and trying to retrieve data from my cards table a NullReferenceException error is thrown. I have checked my code via breakpoints on the following code:
private void FillGrid()
{
DataSet ds = new DataSet();
//get data from session
ds = (DataSet)SessionNavigator.GetDataFromCurrentPage
(PageParams.Customer.DataCards);
if (ds != null)
{
DataView dv = ds.Tables["Cards"].DefaultView;
gridCtrl.RowsCount = dv.Count;
gridCtrl.BindGrid(dv);
}
}
My checks have found that the DataSet ds is indeed not null, and in fact it seems that the application is not recognising the table "Cards", there being no reference to an instance of the object in the table.
Confusingly enough, other tables (such as for users) have no problems whatsoever on the server. Also, manipulating data related to the card objects (such as making transactions and changing points values) are reflected in the SQL Server Management Studio.
I am inexperienced with SQL Server so I may be wrong but I don't think it has anything to do with the database itself.
So SO, are there any glaringly obvious steps that I may have missed when setting up the application that are causing these issues? If so, are there any reference materials that you can recommend?
Edit: After Searching through the PageParams Enumerable and looking closely at the ds Dataset, I have found that ds is not null but has a value of {System.Data.DataSet} containing System.Data.DataTableCollection with a list of size 0.
Found the Answer! Searching through my output when debugging the FillGrid method I found this little line:
System.Data.SqlClient.SqlException: SQL Server blocked access to STATEMENT 'OpenRowset/OpenDatasource' of component 'Ad Hoc Distributed Queries' because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of 'Ad Hoc Distributed Queries' by using sp_configure. For more information about enabling 'Ad Hoc Distributed Queries', see "Surface Area Configuration" in SQL Server Books Online
After that it was just a simple case of allowing Ad Hoc Distributed Queries via sp_configure in the SQL Server Management Studio, resolving the issue completely. This explains why my local copy of the application was working fine but the server was having issues retrieving the relevant DataCards.
It might just be that the name of the table isn't read from the source, and that it's just called "Table1" in your DataSet.
Have you tried to look at the actual content of the dataset?
Related
For a SQL Server instance, to check if a windows user is present and has any access or not one can try various ways as detailed here.
I'm looking for something similar for SQL Server Analysis Services (SSAS) server.
I went into properties of SSAS Server from right-click context menu and on Security tab I can see that there are several windows users already configured:
Is there any way to check from a client application (written in C#) by making some sort of test connection or does SSAS also maintains some metadata database of its own like master database in SQL Server instance (DB engine) which can be queried. I checked the Databases node in SSAS server but I don't see any default databases there:
In the client application I'm working upon, I've windows user name and password as input. In my client application there is a simple winform with two text boxes to take AD user name and password which need to be connected to a SSAS Server. My gut feel is that password is of no relevance here as SSAS supports only Windows integrated authentication mode. My client application would be running under an account which already has access to SSAS server I'm trying to connect.
Update: After getting help from #Vaishali, I'm able to figure out that it is possible to make a test connection to an SSAS server using ADOMD.Net.
Now, the problem here is that the connection string implicitly uses the AD account of the user with which I'm running the client application to connect to the SSAS server. I don't think it would be possible mention an windows AD account user name and password explicitly in the ADOMD.Net connection strings while using Windows Integrated authentication. Even connection strings of SQL Server don't allow mentioning the windows username and password explicitly in the connection string as mentioned here.
Update 2: I have got a lead from one of my friends that it is possible to fire some MDX query on SSAS to get user access details.
Update 3: SSAS server supports only Windows Integrated Security mode of authentication unlike SQL Server DB engine which also supports userid-password based SQL authentication. So, some form of impersonation would be required to fire MDX queries on behalf of other user for which I'm trying to check access on SSAS server through Windows Integrated Security only.
Hmphh...It was quite a journey to really be able to nail it through ADOMD.Net.
Core methodology: The core philosophy is the fact that connection to SSAS server supports only Windows Integrated Security based authentication. The SQL authentication like we do for sa user in SQL Server isn't supported in SSAS.
So, the basic idea was to try to connect to the SSAS server using Windows Integrated Security based authentication and fire an MDX query in the context of the user we are trying to check. If the query gets executed successfully then the user has access. If the query execution returns an error/exception then the user doesn't have access.
Please note that just to be able to open a connection to the SSAS server is not an indicator of user-access due to reasons described here. You must fire a query to check access.
For ADOMD.Net until v12.x:
Now, we know that Windows Integrated Security based authentication always takes the user details from the user-context under which the application/process is running. You can not pass the user credentials in the connection string of ADOMD.Net connection. Here is the code I wrote to accomplish it. You need to refer to Microsoft.AnalysisServices.AdomdClient.dll in your C# project.
using Microsoft.AnalysisServices.AdomdClient;
public static int IsSsasAccessibleToUser(string ssasServerName)
{
var hasAccess = 0;
try
{
using (var adomdConnection = new AdomdConnection($"provider=olap;datasource={ssasServerName};Catalog=myDatabaseName"))
using (var adomdCommand = new AdomdCommand())
{
adomdCommand.CommandText = "SELECT [CATALOG_NAME] AS [DATABASE],CUBE_CAPTION AS [CUBE/PERSPECTIVE],BASE_CUBE_NAME FROM $system.MDSchema_Cubes WHERE CUBE_SOURCE = 1";
adomdCommand.Connection = adomdConnection;
adomdConnection.Open();
adomdCommand.ExecuteNonQuery();
Log("ExecuteNonQuery call succeeded so the user has access");
hasAccess = 1;
}
}
catch (Exception ex)
{
Log("There was an error firing query on the database in SSAS server. so user doesn't have access");
}
return hasAccess;
}
Now, to leverage Windows Integrated Security based authentication we can run this code in two ways:
Out-Proc Impersonation : Put this code inside a console application. Use the "Run as different user" option in the context menu when we right click the exe. Put the credentials of the user Y (let's say) so that application starts in the context of user Y for which we need to validate the access on SSAS server. ADOMD.Net will use user Y's identity while connecting using Windows Integrated Security for SSAS server. If code succeeds the user has access.
In-Proc Impersonation: The other case could be that you are running the application as user X but you want to test the access of user Y. Here effectively you require in-place impersonation while running the above code. For achieving it I used a famous NuGet package "Simple Impersonation" which uses the default .Net library classes WindowsImpersonationContext and WindowsIdentity . Creator of this NuGet package had first posted a great answer here.
Observation in SQL Server Profiler: After you've impersonated user Y, you will clearly see the MDX query getting fired in the context of user Y if you capture the session as shown below:
Caveats and concerns:
One issue that I faced while using this in-proc impersonation is that it doesn't work if the SSAS server is located on the same machine where the application code is running. This is due to the inherent behavior of native LogonUser API (using LOGON32_LOGON_NEW_CREDENTIALS LogonType) which is called during impersonation calls by the NuGete package. You can try other logon types as detailed here which suites you need.
You require password of the user as well along with the domain name and user name to do impersonation.
For ADOMD.Net v13.x onwards
Then, I came across this ChangeEffectiveUser API documentation on MSDN here. But, intellisense wasn't showing this API. Then I found out this API got added in ADOMD.Net with SQL Server 2016 release. There are various ways to get the latest release:
C:\Program Files\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll
I'm not sure who dumps this file at this location. Is it part of Microsoft.Net extensions or SQL Server installation.
In Installation folder of Microsoft SQL Server. I got it at path - C:\Program Files\Microsoft SQL Server\130\Setup Bootstrap\Update Cache\KB3182545\ServicePack\x64\Microsoft.AnalysisServices.AdomdClient.dll
NuGet package here. For some weird reason best known to MS the NuGet package of v13.x of ADOMD.Net has been named Unofficial.Microsoft.AnalysisServices.AdomdClient. Not sure why they introduced a separate NuGet package with Unofficial prefix when this should have been simply the next version of the already existing NuGet package Microsoft.AnalysisServices.AdomdClient present here.
So the new API ChangeEffectiveUser present in latest version on AdomdConnection clas can be used easily to impersonate any user as below:
adomdConnection.Open();
//impersonate the user after opening the connection
adomdConnection.ChangeEffectiveUser("domainName\UserNameBeingImpersonated");
//now the query gets fired in the context of the impersonated user
adomdCommand.ExecuteNonQuery();
Observing Impersonation in SQL Server Profiler: Although one peculiar observation I had in the SQL Server Profiler is that the logs of query being fired still shows the name of the original user with which your application process is running.
So to check whether impersonation is happening or not I removed the access rights of the user domainName\UserNameBeingImpersonated from SSAS server. After that, when I ran the above code again then it resulted in exception whose message clearly states that - the user domainName\UserNameBeingImpersonated doesn't have permission on the SSAS server or the database doesn't exist. This error message clearly suggests that impersonation is working.
Advantages and Backward compatibility of this approach:
Although the API is very recent as it came up with SQL Server 2016 but I was able to use it successfully with SSAS server 2014 as well. So it looks fairly backward compatible.
This API works irrespective of whether your SSAS server is local or remote.
You just require the domain name and user name for doing impersonation. No password require.
What to do if we simply want to check the access on the SSAS server without involving any database present on the SSAS server?
Change the connection string to not involve any database. Remove the Catalog key as following connection string - "provider=olap;datasource={ssasServerName};"
Fire the following query instead to check access - SELECT * FROM $System.discover_locks in the code snippet shown initially in the post.
If you wish to check if user has accessibility to SSAS server, one option you can try with C# is: try connecting SSAS with given user credential, if you succeed, you have access.
If you are looking for roles and security mapped to individual cube database, following link will be usefull.
http://www.lucasnotes.com/2012/09/list-ssas-user-roles-using-powershell.html#comment-form
C# code lines:
import library Microsoft.AnalysisServices.AdomdClient;
and code lines would be:
DataSet ds = new DataSet();
AdomdConnection myconnect = new AdomdConnection(#"provider=olap;datasource=.\SQL20f12");
AdomdDataAdapter mycommand = new AdomdDataAdapter();
mycommand.SelectCommand = new AdomdCommand();
mycommand.SelectCommand.Connection = myconnect;
try
{
myconnect.Open();
}
catch
{
MessageBox.Show("error in connection");
}
Hope this works for you.
I am about to deploy my application and have came into a bit of trouble.
I have the connection string for the database held in the application.settings and need a way to check if the database exists when the program first starts up, and if it doesn't, i need the program to create it before starting the program.
I am assuming it would be a mysql statement to check if db exists, if not create. However, I don't know where or how to do this, can I create a mysql dump of a blank database with tables etc already created and use that?
I have already stored the mysql dll files locally so there is no problem with that, its just creating the database that the string wants to connect to before the application runs so there are no connection errors straight away.
Thanks.
You can do this by running the following SQL statement:
SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = "my_db"
If it doesn't exist from the result set you get returned you can then create it.
This does pose questions regarding MySQL permissions and if your application should have user rights that enable such checking.
Edit in response of comments.
It isn’t clear if you create the connection string or not – I’ll assume the worst and that it is a part of the setup so your client can enter it (if you do know it the process below simplifies.
I would pass the connection string to the constructor of the MySqlConnectionStringBuilder class, this then makes it easy to connect to the database using the MySqlConnection class. I would use the properties from the new instance of the MySqlConnectionStringBuilder class (Server, Host, User etc) to setup the MySqlConnection class.
If the connection didn’t work I would return information to the user and they can update their connection string.
Once I’ve successfully connected to the database I would then use the database name from the Database property of my MySqlConnectionStringBuilder instance to build the query above.
If the command returns NULL the database doesn't exist and then needs creating, if the database does exist then the command will return the name of the database.
Now there are two paths:
It Doesn't exist – It needs creating, I would probably have an external SQL file with the create statements in (can be produced by MySQL dump by using the –nodata option). I would parse this file and execute the create statements
It does exist – I would now check the structure of the database to make sure it is compatible before continuing the installation.
Today, for each customer, we deploy same SSRS reports folder and data source folder.
The difference between these folders are the name of each folder and the connection string of the data source.
We are using Report Server 2008 R2.
Is it possible to maintain only one reports and data source folder and change programmatically its connection string on server-side before the report been rendered?
If not, Is it something that can be achieved by changing some logic in reports?
Today we use "shared data source" option.
This is something we've done in our environment - we maintain one set of reports that can be deployed at any client with their own configuration.
You've got a couple of options here. Since you're using a Shared Data Source this makes things easier as you won't need to define a Data Source for each report.
1. Use the rs.exe utility and a script file
rs.exe at Books Online
This program allows you to create script files (in VB.NET) that can interact with a Report Server Web Service. You create a script file (e.g. Deploy.rss) and call the rs.exe program with various parameters, including any custom ones you define:
rs.exe -i DeployReports.rss -s http://server/ReportServer -v DatabaseInstance="SQL" -v DatabaseName="ReportDB" -v ReportFolder="ClientReports"
So this would call a script DeployReports.rss, connect to http://server/ReportServer, with three user defined parameters which could be used to create a data source and the report folder.
In the scipt file you could have something like this:
Public Sub Main()
rs.Credentials = System.Net.CredentialCache.DefaultCredentials
CreateFolder(reportFolder, "Report folder")
CreateFolder(datasourceFolder, "Data source folder")
CreateDataSource()
End Sub
Which can then make Web Service calls like:
rs.CreateFolder(folderName, "/", Nothing)
'Define the data source definition.
Dim definition As New DataSourceDefinition()
definition.CredentialRetrieval = CredentialRetrievalEnum.Integrated
definition.ConnectString = "data source=" + DatabaseInstance + ";initial catalog=" + DatabaseName
definition.Enabled = True
definition.EnabledSpecified = True
definition.Extension = "SQL"
definition.ImpersonateUser = False
definition.ImpersonateUserSpecified = True
'Use the default prompt string.
definition.Prompt = Nothing
definition.WindowsCredentials = False
Try
rs.CreateDataSource(datasource, datasourcePath, False, definition, Nothing)
Console.WriteLine("Data source {0} created successfully", datasource)
Catch e As Exception
Console.WriteLine(e.Message)
End Try
You haven't specified what version of Reporting Services you're using, so I'm assuming 2008. Please note that there are multiple endpoints that can be used, depending on SQL Server version. The 2005/2008 end point is deprecated in 2008R2 and above but is still usable. Just something to bear in mind when writing your script.
2. Call the SSRS Web Service through an application
Report Server Web Service overview
The same calls that are made from the script above can be made in any other application, too. So you'd just need to add a reference to a Report Server Web Service through WSDL and you can connect to a remote service and call its methods to deploy reports, data sources, etc.
So ultimately you're connecting to the Report Server Web Service, it's just the medium used that you need to think about.
Using a script is easier to get running as it's just running a program from the command line, but writing your own deployment application will certainly give greater flexibility. I would recommend getting the script going, so you understand the process, then migrate this to a bespoke application if required. Good luck!
You can use an Expression Based Connection String to select the correct database. You can base this on a parameter your application passes in, or the UserId global variable. I do believe you need to configure the unattended execution account for this to work.
Note: be careful about the security implications. Realize that if you would pass sensitive data (e.g. passwords) into a parameter, that (a) it will go over the wire, and (b) will be stored in the execution log tables for reporting services.
Context
My appliction uses an SQL database from which it reads my datatables at start of my application. If the application would fail to connect to the SQL DB, I have a local Ms Access .MDB file. I have a separate thread that checks if the local database is outdated.
I have a DataTable which I obtain from my SQL connection --> Verified and working
I can connect to my Access database locally and read from it --> Verified and working
Issue/Question
I'm trying to update my local database by updating it with the DataTable I obtained from my SQL Connection.
public static void UpdateLocalDatabase(string strTableName, OleDbConnection MyConnection, DataTable MyTable)
{
try
{
if (CreateDatabaseConnection() != null)
{
string strQuery = "SELECT * FROM " + strTableName;
OleDbDataAdapter MyAdapter = new OleDbDataAdapter();
OleDbCommandBuilder MyCommandBuilder = new OleDbCommandBuilder(MyAdapter);
MyAdapter.SelectCommand = new OleDbCommand(strQuery, odcConnection);
MyAdapter.UpdateCommand = MyCommandBuilder.GetUpdateCommand();
MyConn.Open();
MyAdapter.Update(MyTable);
MyConn.Close();
}
}
catch { }
}
If I debug this snippet, all variables are what they should be:
strTableName = the correct name for my table
MyConn = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=MyLocation;Persist Security Info=True;JET OLEDB:Database Password=MyPassword;"
MyTable = is the correct table that is also used further on by my application
This process runs through without an error and without using the catch but it does not touch my database, it just doesn't do a thing.
Am I dropping the ball here or just missing the obvious, I have no idea but I browsed many articles and apart for showing the MyAdapter.Update(), there doesn't seem to be much more to it.
Any help is welcome.
Thanks,
Kevin
Does your backup database have to be in access? because if you used SQL Compact Edition it'd be much easier to copy between the two?
Yes, it would either mean attaching it with your installer or just ensuring that all client machines have it pre-installed, it is free however.
if this is an issue then all you need to do (I think, not done it myself)
would be to go to your installer projects properties, click prerequisites and then tick SQL compact so that it will be installed before your application can be used, iv done this before with other frameworks and it just pops up a box with the install shield asking whether they want to download the necessary software and its just one click then it should be done for them.
Do you need a hand on using the compact database also?
One negative by the way is it does lack some higher end features but shouldn't affect average database work
EDIT
if you will be using sql CE you can easily make the databse in VS by clicking data and new data source then following the steps making sure to put sql CE when asked
if it works, you'll end up with an .sdf database
I provided a code snippet that fixed the issue on my related question here: Export SQL DataBase to WinForm DataSet and then to MDB Database using DataSet
Recently our QA team reported a very interesting bug in one of our applications. Our application is a C# .Net 3.5 SP1 based application interacting with a SQL Server 2005 Express Edition database.
By design the application is developed to detect database offline scenarios and if so to wait until the database is online (by retrying to connect in a timely manner) and once online, reconnect and resume functionality.
What our QA team did was, while the application is retrieving a bulk of data from the database, stop the database server, wait for a while and restart the database. Once the database restarts the application reconnects to the database without any issues but it started to continuously report the exception "Could not find prepared statement with handle x" (x is some number).
Our application is using prepared statements and it is already designed to call the Prepare() method again on all the SqlCommand objects when the application reconnects to the database. For example,
At application startup,
SqlCommand _commandA = connection.CreateCommand();
_commandA.CommandText = #"SELECT COMPANYNAME FROM TBCOMPANY WHERE ID = #ID";
_commandA.CommandType = CommandType.Text;
SqlParameter _paramA = _commandA.CreateParameter();
_paramA.ParameterName = "#ID";
_paramA.SqlDbType = SqlDbType.Int;
_paramA.Direction = ParameterDirection.Input;
_paramA.Size = 0;
_commandA.Parameters.Add(_paramA);
_commandA.Prepare();
After that we use ExceuteReader() on this _commandA with different #ID parameter values in each cycle of the application.
Once the application detects the database going offline and coming back online, upon reconnect to the database the application only executes,
_commandA.Prepare();
Two more strange things we noticed.
1. The above situation on happens with CommandType.Text type commands in the code. Our application also uses the same exact logic to invoke stored procedures but we never get this issue with stored procedures.
2. Up to now we were unable to reproduce this issue no matter how many different ways we try it in the Debug mode in Visual Studio.
Thanks in advance..
I think with almost 3 days of asking the question and close to 20 views of the question and 1 answer, I have to conclude that this is not a scenario that we can handle in the way we have tried with SQL server.
The best way to mitigate this issue in your application is to re-create the SqlCommand object instance again once the application detects that the database is online.
We did the change in our application and our QA team is happy about this modification since it provided the best (or maybe the only) fix for the issue they reported.
A final thanks to everyone who viewed and answered the question.
The server caches the query plan when you call 'command.Prepare'. The error indicates that it cannot find this cached query plan when you invoke 'Prepare' again. Try creating a new 'SqlCommand' instance and invoking the query on it. I've experienced this exception before and it fixes itself when the server refreshes the cache. I doubt there is anything that can be done programmatically on the client side, to fix this.
This is not necessarily related exactly to your problem but I'm posting this as I have spent a couple of days trying to fix the same error message in my application. We have a Java application using a C3P0 connection pool, JTDS driver, connecting to a SQL Server database.
We had disabled statement caching in our the C3P0 connection pool, but had not done this on the driver level. Adding maxStatements=0 to our connection URL stopped the driver caching statements, and fixed the error.