Sometimes when my customers turn on or restart their computer, and open my vb.net application directly, the application opens before SQL Server has started completely.
This results in many unexpected behaviors. To avoid this situation, I need to start a splash screen and check the SQL Server state within it, and only when SQL Server state indicates that it is loaded completely, can I run the whole application.
The question is: how to check the SQL Server state, whether it is finished loading or not? The whole SQL Server, not the database.
You can instantiate a timer in your splash form that checks if it can log into the database every one second (or whatever interval you like). I'd invoke the splash form as modal so the calling app can't continue until the splash form has detected the connection and closed itself.
At the very least you need the server name to check the connection for. If it is using a named instance then the server name should also include the instance name in the format "myserver\myinstance".
I've encapsulated the connection checking logic in the 3 overloaded functions IsConnected. You can use these functions in your splash form to check connection from the timer tick. (Each depends on the next). You can use whichever function overload is suitable based on the input items you have available.
For the first overload, if the app is running under a Windows security context that can connect to the db server then you don't need to provide the username and password (pass as empty string), otherwise you need to provide those credentials needed to login to the db server. Or you can provide your own connection string or connection object for the other overloads.
(code within the splash form)...
Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick
If Me.IsConnected("(local)\SQL2008R2", "", "") Then Me.Close()
End Sub
Public Function IsConnected(ServerName As String, UserID As String, Password As String) As Boolean
Dim connStr As String = String.Format("Data Source={0}", ServerName)
If Not String.IsNullOrEmpty(UserID) Then
connStr &= String.Format(";User ID={0};Password={1}", UserID, Password)
Else
connStr &= ";Integrated Security=True"
End If
Return IsConnected(connStr)
End Function
Public Function IsConnected(Connection As String) As Boolean
Static conn As SqlConnection
If conn Is Nothing Then
conn = New SqlConnection(Connection)
conn.Open()
End If
Return IsConnected(conn)
End Function
Public Function IsConnected(ByRef Conn As SqlConnection) As Boolean
If Conn IsNot Nothing Then Return (Conn.State = ConnectionState.Open)
Return False
End Function
I'd invoke the splash form from the main app as a modal dialog, as such, so the app is blocked until the connection is detected.
(from the calling app form...)
frm_Splash.ShowDialog()
Related
I need to open the connection for Entity Framework without a connection string.
Due to a security layer that we are using I'm, we don't allow to connect to SQL Server using connection string, So we have a DLL that returns an opened SqlConnection.
EF version: 6.2.0
Error:
MetadataWorkspace must have EdmItemCollection pre-registered.
I tried to give the entityConnection as SqlConnection but I get an error.
Sample code:
Public Shared Function getEntityConnection() As EntityConnection
Dim workspace As New MetadataWorkspace()
Return New EntityClient.EntityConnection(workspace, AppCommon.AppFunctions.AppGetSQLCon(True))
End Function
AppCommon.AppFunctions.AppGetSQLCon(True) is the function which returns the SqlConnection instance.
But it's not working, does anyone have a solution for this issue?
Finally I found the solution for connecting the entityframework without a connection string, So what you need to is the following:
change the constructor of the entity `DbContext' to recive the connection from a function like this:
Public Sub New()
MyBase.New(getEntityConnection(), False)
End Sub
Then inside that function return an entity-connection object from an open sqlconnection obbject as the following:
Public Shared Function getEntityConnection() As EntityConnection
Dim workspace As New MetadataWorkspace(New String() {"res://*/"}, New Assembly() {Assembly.GetExecutingAssembly()})
Return New EntityClient.EntityConnection(workspace, getSqlConnectionObject())
End Function
Now your entityframework is connected to the database without a connection string
I'm busy testing SSRS to see if it's a viable alternative to our current reporting solution. I've set up SSRS on my local machine and have developed a working report using SQL Server Report Builder. Now what I'm trying to do is to call the report from within a WinForms application and display it in a ReportViewer control. The problem is that I've set up SQL Server to use SQL Server Authentication and I'm struggling to figure out how to connect to it programmatically.
The code I've pieced together so far looks like this:
Imports Microsoft.Reporting.WinForms
Public Class frmMain
Public v_report_name As String = "TestReport"
Public v_report_server As String = "http://elnah-ict-dt006:80"
Public v_report_path As String = "/reports_SSRS/"
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'create parameter array
Dim paramlist As New List(Of Microsoft.Reporting.WinForms.ReportParameter)
'create a specific parameter required by the report
Dim param1 As New Microsoft.Reporting.WinForms.ReportParameter("ClientID")
'add values to the parameter here we use a variable that holds the parameter value
param1.Values.Add("0279")
'add parameter to array
paramlist.Add(param1)
'Set the processing mode for the ReportViewer to Remote
ReportViewer1.ProcessingMode = ProcessingMode.Remote
'use the serverreport property of the report viewer to select a report from a remote SSRS server
ReportViewer1.ServerReport.ReportServerUrl = New System.Uri(v_report_server)
ReportViewer1.ServerReport.ReportPath = v_report_path & v_report_name
'select where the report should be generated with the report viewer control or on the report server using the SSRS service.
'Me.ReportViewer1.ProcessingMode = Microsoft.Reporting.WinForms.ProcessingMode.Remote
'add the parameterlist to the viewer
ReportViewer1.ServerReport.SetParameters(paramlist)
Me.ReportViewer1.RefreshReport()
End Sub
End Class
When it hits the SetParameters line towards the bottom, it gets the following error message:
Microsoft.Reporting.WinForms.Internal.Soap.ReportingServices2005.Execution.RSExecutionConnection.MissingEndpointException
HResult=0x80131500
Message=The attempt to connect to the report server failed. Check your connection information and that the report server is a compatible version.
Source=Microsoft.ReportViewer.WinForms
I've tried to find examples of how to set the username and password but from what I can tell, most examples are focused on using Windows Authentication. I've tried the following line but it doesn't work:
ReportViewer1.ServerReport.ReportServerCredentials = New ReportServerCredentials("SA", "mypassword")
I haven't worked in VB.NET for ages so please excuse any obvious errors.
Here's some code from a Web Forms project I was part of the team for recently:
private void SetCredentials()
{
var userName = ConfigurationManager.AppSettings["SSRSUserName"];
var passwordEncrypted = ConfigurationManager.AppSettings["SSRSUserPasswordEncrypted"];
var passwordPlainText = SI.Crypto3.Crypto.Decrypt(passwordEncrypted, PASSPHRASE);
var domain = ConfigurationManager.AppSettings["SSRSUserDomain"];
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(passwordPlainText) && !string.IsNullOrEmpty(domain))
{
this.EventsHubReportViewer.ServerReport.ReportServerCredentials = new ReportServerCredentials(userName, passwordPlainText, domain);
}
}
That's C# but hopefully you can see that the important part is that last line. I think that the equivalent in your case should be:
ReportViewer1.ServerReport.ReportServerCredentials = New ReportServerCredentials(userName, password, domain)
The domain value can be an empty String if your on the same domain as the server.
EDIT:
I looked more closely and the ReportServerCredentials class that code is using is one of our own. In your case, you can use the Microsoft.ReportViewer.WinForms.ReportServerCredentials class, which I don't think has a constructor like that. Looking at the documentation for the NetworkCredentials property of that type indicates that you need to do this:
Dim credentials As New NetworkCredential(userName, password, domain)
ReportViewer1.ServerReport.ReportServerCredentials.NetworkCredentials = credentials
I have developed winform application using VB.NET. The application is deployed in the machine which is connected to wireless network. The machine is in the car(moving object).
The application has DataGridView loaded with the data get from MSSQL Server(in Remote machine). The data is refreshed for every 5 seconds.
I have used the NetworkAvailabilityChanged event to detect the network status. If the Network is available, then I retrieve the data from the table.
Code:
AddHandler NetworkChange.NetworkAvailabilityChanged, AddressOf NetworkStateChangeHandler
Public Sub NetworkStateChangeHandler(ByVal sender As Object,
ByVal e As NetworkAvailabilityEventArgs)
If e.IsAvailable = True Then
g_bNetworkAlive = True
Else
g_bNetworkAlive = False
End If
End Sub
private Sub GetData()
If g_bNetworkAlive = True
'code to get the data from table
End If
End Sub
Issue:
If the car movers out of the out of the network, the NetworkAvailabilityChanged event is not fired. so it throws the following error for every 5 seconds and application gets crashed.
A network-related or instance-specific error occurred while establishing
a connection to SQL Server. The server was not found or was not accessible.
Temporary fix: I have made Ping request to the SQL server machine for every 5 seconds to detect the network status. It affects the application's performance.
Note: If I manually switch off the Wifi, the NetworkAvailabilityChanged event is fired. The issue is only when the car moves out of the network.
Is there any some other feasible solution to detect the wireless network status?
Indeed there is.
Why send http request to some dummy website when you can check if you are connected to wifi or not locally.
Use ManagedWifi APIs, This will eliminate the slowdown you are facing.
//Create object at the beginning of your application
WlanClient wlanClient = new WlanClient();
//You this code to check wifi availability wherever you need
foreach (WlanInterface _interface in wlanClient.Interfaces)
{
If (_interface.CurrentConnection.wlanAssociationAttributes.dot11Ssid.SSID!=null)
{
// You are connected to wifi
}
}
EDIT: In case you you are not finding dll, direct link. Just reference the Dll and Voila! you are done.
Change your code as
Private Sub GetData()
If My.Computer.Network.IsAvailable AndAlso g_bNetworkAlive
' code to get the data from table
End If
End Sub
You also can check if you have an internet connection by using WebRequest like this:
Private Sub GetData()
If HasInternet AndAlso g_bNetworkAlive Then
'code to get the data from table
End If
End Sub
Public Shared Function HasInternet As Boolean
Return Not (HttpGet("http://www.google.com/") = "Error")
End Function
Public Shared Function HttpGet(url As String) As String
Dim request As WebRequest = WebRequest.Create(url)
request.Method = "GET"
Try
Dim response As WebResponse = request.GetResponse()
Dim dataStream As Stream = response.GetResponseStream()
Dim reader As New StreamReader(dataStream)
Dim responseFromServer As String = reader.ReadToEnd()
reader.Close()
dataStream.Close()
response.Close()
Return responseFromServer
Catch ex As Exception
Return "Error"
End Try
End Function
And you can check the sql connection by using the following function:
Private Function IsDatabaseConnected() As Boolean
Try
Using sqlConn As New SqlConnection("YourConnectionString")
sqlConn.Open()
Return (sqlConn.State = ConnectionState.Open)
End Using
Catch e1 As SqlException
Return False
Catch e2 As Exception
Return False
End Try
End Function
I would simply use try..catch, so if you get exception, you can know, based on its id, why the code failed, and decide what to do next, and finally you can execute some more code regardless of data being retrieved or not
Private Sub GetData()
Try
'code to get the data from table
Catch ex As Exception
' Show the exception's message.
MessageBox.Show(ex.Message)
' Show the stack trace, which is a list of methods
' that are currently executing.
MessageBox.Show("Stack Trace: " & vbCrLf & ex.StackTrace)
Finally
' This line executes whether or not the exception occurs.
MessageBox.Show("in Finally block")
End Try
End Sub
In .NET I simply use Application Name = MyApp inside the connection string, but when using ADO connection through VBA the Activity Monitor of the SQL Server Management Studio always shows Microsoft Office 2010 in Processes on the Application column no matter what name I set on the VBA code.
conn.ConnectionString = "UID=" & UID & ";PWD=" & PWD & ";DSN=" & DSN & _
";Application Name = MyApp"
How can I set the application name for monitoring purposes?
Ahh I see VBA connection string doesn't support the Application Name attribute. It simply isn't being recognized when used within VBA. The only way I can think of solving this at the moment it's to return an ADODB.Connection object from a COM C# library.
Your own COM library would return an ADODB.Connection object with a predefined connection string which seem to work in .NET. You will be connecting to the database using a VBA ADODB.Connection object but with a substituted object reference. Instead of
Set cn = new ADODB.Connection you will use a GetConection() method exposed by your own library.
Dim cn as ADODB.Connection
Set cn = yourCOMlibrary.GetConnection
here are the steps
Download and install Visual Studio Express for Windows (FREE)
Open it as Administrator and create a New Project. Select Visual C# then Class Library and rename it to MyConnection
In the Solution Explorer, rename Class1.cs to ServerConnection.cs
Right click your MyConnection project in the Solution Explorer and select Add Reference
Type activeX in the search box and tick the Microsoft ActiveX Data Objects 6.1 Library
Copy and paste the below code into the ServerConnection.cs completely replacing whatever is in the file.
using System;
using System.Runtime.InteropServices;
using System.IO;
using ADODB;
namespace MyConnection
{
[InterfaceType(ComInterfaceType.InterfaceIsDual),
Guid("32A5A235-DA9F-47F0-B02C-9243315F55FD")]
public interface INetConnection
{
Connection GetConnection();
void Dispose();
}
[ClassInterface(ClassInterfaceType.None)]
[Guid("4E7C6DA2-2606-4100-97BB-AB11D85E54A3")]
public class ServerConnection : INetConnection, IDisposable
{
private Connection cn;
private string cnStr = "Provider=SQLOLEDB; Data Source=SERVER\\DB; Initial Catalog=default_catalog; User ID=username; Password=password;Application Name=MyNetConnection";
public Connection GetConnection()
{
cn = new Connection();
cn.ConnectionString = cnStr;
return cn;
}
public void Dispose()
{
cn = null;
GC.Collect();
}
}
}
Locate the cnStr variable in the code and UPDATE your connection string details.
Note: if you are unsure about the connection string you should use see ALL CONNECTION STRINGS
Click on TOOLs in Visual Studio and CREATE GUID
Replace the GUIDs with your own and remove the curly braces so they are in the same format as the ones you see now from the copied code
Right click MyConnection in the Solution Explorer and select Properties.
Click the Application tab on the left side, then Assembly Info and tick Make Assembly COM-Visible
Click the *Build* from the menu on the left and tick Register For COM Interop
Note: If you are developing for 64-bit Office then make sure you change the Platform Target on the Build menu to x64! This is mandatory for 64-bit Office COM libraries to avoid any ActiveX related errors.
Right click MyConnection in the Solution Explorer and select Build from the menu.
If everything went OK then your MyConnection.dll and MyConnection.tlb should be successfully generated. Go to this path now
C:\Users\username\desktop\
or wherever you saved them
and you should see your files.
Now open Excel and go to VBE. Click Tools and select References.
Click the Browse button and navigate to the MyConnection.tlb.
Also, add references to Microsoft ActiveX Object 6.1 Library - this is so you can use ADODB library.
Now right click anywhere in the Project Explorer window and Insert a new Module
copy and paste the below code to it
Option Explicit
Sub Main()
Dim myNetConnection As ServerConnection
Set myNetConnection = New ServerConnection
Dim cn As ADODB.Connection
Set cn = myNetConnection.GetConnection
cn.Open
Application.Wait (Now + TimeValue("0:00:10"))
cn.Close
Set cn = Nothing
myNetConnection.Dispose
End Sub
Open SQL Server Management Studio, right click the server and select Activity Monitor
dont close this window
Go back to Excel and hit F5 or hit the green play button on the ribbon.
now switch back to SSMS ( SQL Server Management Studio )
and wait for your custom connection name to appear! :)
Here we go! That was easy, wasn't it? :)
This is what is happening.
You are returning an ADODB Connection object from you C# COM library by using myNetConnection.GetConnection function
Dim myNetConnection As ServerConnection
Set myNetConnection = New ServerConnection
Dim cn As ADODB.Connection
Set cn = myNetConnection.GetConnection
It's almost like saying Set cn = new ADODB.Connection but with predefined connection string which you did in your C# code.
You can use the cn object like a normal ADODB.Connection object within VBA now.
Remember to always .Close() the ADODB.Connection. A good programmers practice is to always close anything you open - streams, connections, etc.
You can rely on the Garbage Collector to free references/ memory but I also wrote a Dispose() method for you so you can force the GC to run. You can do that to immediately get rid of the Connection so it does not hang in the SSMS as opened.
Remember to use myNetConnection.Dispose along with the cn.Close and you'll be fine.
Note:
This is how I would do it if any one thinks this is wrong or needs to be updates (as being unstable or unsafe) please leave a comment.
Well, I hope this will be helpful to anyone in the future :)
The correct keyword to set the application name in an ADODB connection string in VBA is APP, not Application Name.
Example connection string, copied from an MS Access app I'm working on:
DRIVER={SQL Server};SERVER=xxxx;DATABASE=xxxx;Trusted_Connection=Yes;APP=xxxx
I need to "impersonate" a user in a VB.NET 2008 WinForms application, so that the application can accept the Active Directory login of any user on a PC regardless of who is actually logged in to Windows. I want the application's My.User to be the AD account of the person who logged in to the application. I succeeded in this with the following code:
Private Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As String, ByVal lpszDomain As String, _
ByVal lpszPassword As String, ByVal dwLogonType As Integer, _
ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Boolean
Const LOGON32_LOGON_INTERACTIVE As Long = 2
Const LOGON32_LOGON_NETWORK As Long = 3
Const LOGON32_PROVIDER_DEFAULT As Long = 0
Const LOGON32_PROVIDER_WINNT35 As Long = 1
Const LOGON32_PROVIDER_WINNT40 As Long = 2
Const LOGON32_PROVIDER_WINNT50 As Long = 3
' Influenced from the example at http://aspalliance.com/39
Public Shared Function Login(ByVal uid As String, ByVal pwd As String) As Boolean
' Get the user's domain name.
Dim domainName As String = My.User.Name.Substring(0, My.User.Name.IndexOf("\"))
' This token is returned by the LogonUser API call (variable is passed ByRef).
Dim token As IntPtr
If LogonUser(uid, domainName, pwd, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, token) Then
' Added this line per response to this question:
WindowsIdentity.Impersonate(token)
' If the login succeeds, then impersonate that user by changing CurrentPrincipal.
Dim wi As New Principal.WindowsIdentity(token)
Dim wp As New Principal.WindowsPrincipal(wi)
My.User.CurrentPrincipal = wp
Return True
Else
Return False
End If
End Function
However, the application uses a .DLL with the Data Access Layer which is connecting to SQL Server 2000. It appears that SQL Server, using "Integrated Security=SSPI" in the connection string, is receiving the login of the account logged in to Windows and not the account returned My.User.CurrentPrincipal.Identity, when stepping through the code, in both the WinForms app code and the .DLL's app code.
Both the WinForms app and .DLL code properly recognize My.User.CurrentPrincipal.Identity as the account logged in to the app, not Windows. It's just not propagating to SQL Server. This is evidenced by Stored procedures writing SUSER_SNAME() to a table's column in T-SQL.
Can anyone see what I'm going wrong?
EDIT: I've added the line WindowsIdentity.Impersonate(token) as stated, but now when my .DLL tries to create an SQL Server connection it throws this error:
Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'.
You need to call WindowsIdentity.Impersonate();:
If LogonUser(...) Then
WindowsIdentity.Impersonate(token)