It's possible to use OleDbConnections with the Script Component? - sql-server

I'm building an ssis package and I wish to use an existing OleDbConnection inside the Script Component. Here is my code:
public override void AcquireConnections(object Transaction)
{
base.AcquireConnections(Transaction);
cm = this.Connections.Connection;
con = (OleDbConnection)cm.AcquireConnection(Transaction);
MessageBox.Show(con.ToString());
}
When I close BIDS, i get the following message:
"System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' to class type 'System.Data.OleDb.OleDbConnection'. Instances of types that represent COM components cannot be cast to types that do not represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface."
The same code works fine with an Ado.Net connection. Can I use OleDbConnection here or Script Component only supports Ado.Net?
Thanks in advance.

As mentioned in the MSDN
You cannot call the AcquireConnection method of connection managers that return unmanaged objects, such as the OLE DB connection manager and the Excel connection manager, in the managed code of a Script task.
You need to use the ADO.NET connection manager if you want to use Aquire Connection method
in order to use OLEDB connection add a reference to Microsoft.SqlServer.DTSRuntimeWrap and try the below code
ConnectionManager cm = Dts.Connections["oledb"];
IDTSConnectionManagerDatabaseParameters100 cmParams =
cm.InnerObject as IDTSConnectionManagerDatabaseParameters100;
OleDbConnection conn = cmParams.GetConnectionForSchema() as OleDbConnection;
MSDN Link

Just in case someone googled this and couldn't find a real solution, you have to override the AcquireConnections, PreExceute and ReleaseConnections methods in order to use an OleDbConnection. The trick is the ConnectionString property:
OleDbConnection con;
OleDbCommand cmd;
IDTSConnectionManager100 connMgr;
/*Here you prepare the connection*/
public override void AcquireConnections(object Transaction)
{
base.AcquireConnections(Transaction);
connMgr = this.Connections.YourConnName;
con = new OleDbConnection(connMgr.ConnectionString);
}
/*Here you prepare the sql command and open the connection*/
public override void PreExecute()
{
base.PreExecute();
cmd = new OleDbCommand("Some Select", con);
cmd.CommandType = CommandType.Text;
con.Open();
}
/*Here you execute your query for each input row*/
public override void Entrada0_ProcessInputRow(Entrada0Buffer Row)
{
OleDbDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
/*Do your stuff*/
}
}
/*And here you release the connection*/
public override void ReleaseConnections()
{
base.ReleaseConnections();
connMgr.ReleaseConnection(con);
}
HTH

Thanks praveen.
I found the relevant part in your link:
"If you must call the AcquireConnection method of a connection manager that returns an unmanaged object, use an ADO.NET connection manager. When you configure the ADO.NET connection manager to use an OLE DB provider, it connects by using the .NET Framework Data Provider for OLE DB. In this case, the AcquireConnection method returns a System.Data.OleDb.OleDbConnection instead of an unmanaged object. To configure an ADO.NET connection manager for use with an Excel data source, select the Microsoft OLE DB Provider for Jet, specify an Excel file, and enter Excel 8.0 (for Excel 97 and later) as the value of Extended Properties on the All page of the Connection Manager dialog box."
Thanks!

Related

how to open EntityConnection without connection string

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

SSIS upgrade from 2008R2 to 2017 - script task fails when connecting to OLEDB data source

I'm in the process of upgrading a data warehouse from SQL Server 2008R2 to 2017. After the upgrade, every script task is throwing the same error trying to connect to a logging database.
The task connects to a database via an OLEDB connection manager, then writes some meta data to a logging table. It's failing on making the initial connection. I'm hoping it's a fairly simple invocation error, but I'm a SQL guy, not a VB guy, and I'm not seeing the issue.
I found this question, Missing library to reference OLEDB connection types, but the script isn't invoking the AcquireConnection() method, so I don't think it's applicable. Or if it is, I'm not seeing how.
This is the code, through the point of failure.
Imports System
Imports System.Data
Imports System.Math
Imports Microsoft.SqlServer.Dts.Runtime
Imports System.Data.OleDb
Imports System.Collections
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
<Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute> _
<System.CLSCompliantAttribute(False)> _
Partial Public Class ScriptMain
Inherits Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
Enum ScriptResults
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
End Enum
Public Sub Main()
Dim fireAgain As Boolean
Dim pkgExecVar As Variables
Dim cm As ConnectionManager
Dim cmParam As IDTSConnectionManagerDatabaseParameters100
Dim conn As OleDb.OleDbConnection
Dim cmd As OleDbCommand = New OleDbCommand()
pkgExecVar = Nothing
cm = Dts.Connections("Configuration_Metadata_Logging")
cmParam = CType(cm.InnerObject, IDTSConnectionManagerDatabaseParameters100)
conn = CType(cmParam.GetConnectionForSchema(), OleDb.OleDbConnection)
When I step through the code, the second line from the bottom throws an exception.
cmParam = CType(cm.InnerObject, IDTSConnectionManagerDatabaseParameters100)
System.InvalidCastException
HResult=0x80004002
Message=Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.SqlServer.Dts.Runtime.Wrapper.IDTSConnectionManagerDatabaseParameters100'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{624862CB-55F9-4A92-965F-62BC4935296A}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
If there's anything else I can add to the question to help, please let me know.
EDIT: The OLEDB connection manager is using SQLNCLI11.1. Other components of the package (Execute SQL & Dataflow Tasks) are able to connect successfully.
EDIT II: In order to make this more searchable, here's the initial error the package throws, before adding a breakpoint to the script task and stepping through it.
Exception has been thrown by the target of an invocation.
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
at Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTATaskScriptingEngine.ExecuteScript()
Update. I found the source of that code, and it's a blog post by Matt Mason called Accessing OLEDB Connection Managers in a Script. It's still undocumented and probably got broken some time between SQL 2008 and 2017.
The supported ways to do this are documented here: Connecting to Data Sources in the Script Task
The only ways to get a System.Data.OleDbConnection in a script task are
1) To use an ADO.NET Connection Manager and configure it to use the ADO.NET Provider for OleDb, like this:
Dim cm As ConnectionManager = Dts.Connections("Configuration_Metadata_Logging_adonet_oledb")
Dim conn As OleDb.OleDbConnection = CType(cm.AcquireConnection(Nothing), OleDb.OleDbConnection)
2) Use an OleDb Connection Mamanger and use its ConnectionString to create a new OleDbConnection (which you are also responsible for closing in your Script task). Like this:
Dim cm2 As ConnectionManager = Dts.Connections("Configuration_Metadata_Logging_oledb")
Using conn2 As New OleDb.OleDbConnection(cm2.ConnectionString)
conn2.Open()
End Using

How to set "Application Name" in ADODB connection string

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

Change Connection String in App.config at runtime

The code below serves to change connection string in App.config at runtime, I found it here but this code did not work for me on Visual Studio 2010 and SQL Server 2008, I could not open the connection to the Northwind database.
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Windows.Forms;
using System.Xml;
namespace MyNameSpace
{
public partial class FrmConnectionTest : Form
{
public FrmConnectionTest()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
//Constructing connection string from the inputs
StringBuilder Con = new StringBuilder("Data Source=");
Con.Append(TxtServer.Text);
Con.Append(";Initial Catalog=");
Con.Append(TxtDatabase.Text);
Con.Append(";Integrated Security=SSPI;");
string strCon = Con.ToString();
updateConfigFile(strCon);
//Create new sql connection
SqlConnection Db = new SqlConnection();
//to refresh connection string each time else it will use previous connection string
ConfigurationManager.RefreshSection("connectionStrings");
Db.ConnectionString = ConfigurationManager.ConnectionStrings["con"].ToString();
//To check new connection string is working or not
SqlDataAdapter da = new SqlDataAdapter("select * from employee");
DataTable dt = new DataTable();
da.Fill(dt);
CmbTestValue.DataSource = dt;
CmbTestValue.DisplayMember = "EmployeeID";
}
catch (Exception E)
{
MessageBox.Show(ConfigurationManager.ConnectionStrings["con"].ToString() + ".This is invalid connection", "Incorrect server/Database");
}
}
public void updateConfigFile(string con)
{
//updating config file
XmlDocument XmlDoc = new XmlDocument();
//Loading the Config file
XmlDoc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
foreach (XmlElement xElement in XmlDoc.DocumentElement)
{
if (xElement.Name == "connectionStrings")
{
//setting the coonection string
xElement.FirstChild.Attributes[2].Value = con;
}
}
//writing the connection string in config file
XmlDoc.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
}
}
}
Using Visual Studio 2010 and SQL Server2008, I got 2 errors for the next line:
SqlDataAdapter da = new SqlDataAdapter("select * from employee");
Error 1 The best overloaded method match for 'System.Data.SqlClient.SqlDataAdapter.SqlDataAdapter(System.Data.SqlClient.SqlCommand)' has some invalid arguments
Error 2 Argument 1: cannot convert from 'string' to 'System.Data.SqlClient.SqlCommand'
Is there any solution to this issue? Thank you.
The error is telling you that you are passing incorrect parameters to your SqlDataAdapter. I think the proper call would be:
SqlDataAdapter da = new SqlDataAdapter("select * from employee", Db);
Edit
It looks like you're creating your connection string from within your program, saving it to your config file, then reading it out of our config file right before you create your SqlDataAdapter. So, when you debug this line:
Db.ConnectionString = ConfigurationManager.ConnectionStrings["con"].ToString();
Double check that Db.ConnectionString actually contains a connection string.
The other thing to do is open up your SQL Server Management Studio and confirm you can connect to the Northwind database from there. Including/alternatively, in Visual Studio, open your "Server Explorer" window and confirm you can create a Data Connection to Northwind by clicking Add Connection and then setting the connection property window to your server and dropping down the combobox to see if it populates with your databases:
Take a look at the available constructors of the SqlDataAdapter class.
There is no constructor overload that accepts just an SQL String.
You need to use one of the other overloads.
For example, there is one that needs an SQL String and a SqlConnection object.
To use it, change your code like this:
SqlDataAdapter da = new SqlDataAdapter("select * from employee", Db);
EDIT:
As BradRem already mentioned in his comment, try a different connection string.
If his example doesn't work for you, you can find more possible examples at http://connectionstrings.com/sql-server-2008.
Do you really have a database called Northwind on your server?
Does the Windows user on your current machine have permissions on the server to access the database? (that's what Integrated Security=SSPI means - your current Windows user is used to access the database!)

How can I get SQL Server database properties from C# code?

I've a C# 4.0 console application and a SQL Server 2008 database up and running on database server.
Does somebody know if there is a way to get the database properties (like "Database Read-Only" ) from the C# code?
My guess is it should be, but I was not able to find out the accurate solution.
Thanks in advance
There are at least two ways to do it. One is to use the Database Metadata tables the other to use SQL Management objects in both cases there's lot of data there so you really need to know what you want. For example this is how you would get the "Database Read-Only" property you mentioned.
Using a SqlCommand against the MetaData
using (SqlConnection cnn = new SqlConnection("Data Source=.;Initial Catalog=master;Integrated Security=SSPI;"))
{
SqlCommand cmd = new SqlCommand("SELECT is_read_only FROM sys.filegroups",cnn);
cnn.Open();
var isReadOnly = cmd.ExecuteScalar();
Console.WriteLine(isReadOnly );
}
Using SMO
using System;
using Microsoft.SqlServer.Management.Smo;
namespace SMO_Test
{
class Program
{
static void Main(string[] args)
{
Server srv = new Server(); //Connection to the local SQL Server
Database db = srv.Databases["master"];
foreach (FileGroup fg in db.FileGroups)
{
Console.WriteLine(fg.ReadOnly);
}
}
}
}

Resources