Software Development
Blogs and Discussion
developer.*
Books Articles Blogs Subscribe d.* Gear About Home

Reusable Functions for Reading and Writing Variables in SSIS Script Tasks

One of the first of many pains-in-the-butt that SQL Server Integration Services developers will encounter is how from a Script Task in the Control of Flow to write to and read from package variables. I have blogged a bit about the topic of SSIS variables in script tasks in the past, but in this post I'd like to share two custom functions that I copy and paste into all of my script tasks to simplify dealing with variables. If you use these functions you will not need the Script Task Edtior's ReadWriteVariables field to make your package variables available to the script.

First, the ReadVariable function:

Private Function ReadVariable(ByVal varName As String) As Object
Dim result As Object

Try
Dim vars As Variables
Dts.VariableDispenser.LockForRead(varName)
Dts.VariableDispenser.GetVariables(vars)
Try
result = vars(varName).Value
Catch ex As Exception
Throw ex
Finally
vars.Unlock()
End Try
Catch ex As Exception
Throw ex
End Try

Return result
End Function

And WriteVariable():

Private Sub WriteVariable(ByVal varName As String, ByVal varValue As Object)
Try
Dim vars As Variables
Dts.VariableDispenser.LockForWrite(varName)
Dts.VariableDispenser.GetVariables(vars)
Try
vars(varName).Value = varValue
Catch ex As Exception
Throw ex
Finally
vars.Unlock()
End Try
Catch ex As Exception
Throw ex
End Try
End Sub

The functions are pretty straightforward once you've grokked the VariableDispenser and locking concepts, but if you'd prefer not to grok, these functions make it easy to remain blissfully ignorant. If you read the code closely you will see that this is not necessarily the optimal technique performance-wise because it only deals with one variable at a time. Notice that the call to VariableDispenser.GetVariables() only gets one variable. If your Script Task is reading and writing from many variables, then accessing them for reading and writing one at a time in this way might not be the most efficient technique--but my guess is you'd have a hard time measuring the impact of any theoretically better performing techniques, and the price you'd pay in the loss of code simplicity and understandability would outweigh any possible performance benefits.

(This goofiness, in my interpretation, is anyway all a side effect of the clumsy and awkward integration between SSIS, BIDS, and this VB.NET scripting engine--but that's a whole other rant. Even useful functions like these cannot save you if you are unfortunate enough to forget the exact spelling of one of your package variables while you're coding your script; you'll still have to go down and up the stack of modal dialogs to go look it up and come back to your code--OK, I must resist getting full into this rant...)

Getting back on track, here is an example of using the ReadVariable() function:

Public Sub Main()
'Assume success
Dts.TaskResult = Dts.Results.Success

Dim jobId As Integer
jobId = CType(ReadVariable("JobId"), Integer)
End Sub

Notice that the variable name is passed as a string. Notice also that the value that comes back is a System.Object. Notice how you have to use CType to cast the value to the type you really want. Notice also how this code makes a big assumption that CType is going to work just fine--that is, we're assuming that the JobId variable will indeed hold a valid Integer value. If there's any doubt about this, I advise additional code to test the value before attempting to cast it.

(If you're not familiar with the technique of setting Dts.TaskResult at the top of Sub Main, check out my blog post on why I use this convention.)

Here's a WriteVariable() example:

WriteVariable("SomeBooleanVariable", True)

Notice how the second argument of WriteVariable() is also a System.Object. This means you can pass an valid VB.NET value. However, you still need to be cognizant of what the "data type" of the variable is as it is defined in the package. That is, if it's a numeric variable, for example, don't try to write an alphanumeric string to it.

Hope this helps,
Dan

Variables in Script Component

I just spent hours chasing my tail with a Variable and SSIS Script Component. If you set the Variable.Expression to True, you can not change its Value in a Script Component. If you try to, it appears to set the value to Nothing.

One of the good blogs about

One of the good blogs about the usage of variables!

I had a scenario like I have to prepare mail messages dynamically when an error happens in a script component present in the control flow. So the onerror event handler, of the control flow script compnent had one more script component for preparing the mail message. Both the script components were trying to use a package level variable errorDescription.

I was using the second method :
a) passing errorDescription as readwrite parameter in the control flow script component
b) passing errorDescription as readonly parameter in the mail message preparing script component

When the control flow script component failed, the mail message script component was called. But the mail message script component remained waiting for aquiring a lock for reading the errorDescription. Finally it ended up unable to handle a deadlock over the package level variable errorDescription.

Then I read this blog by Daniel were he was showing how to write to variable and immediately unlocking it. So I modified the control flow script component. Removed the errorDescrptioon as a readwrite variable and followed out the first methd shown in this blog for writing into variable. It worked out !!!

Thanks!

Thanks, Daniel! This is exactly what I needed for a project I'm working on. Saved me a bunch of time.

- Mike

Yeah that rules

Thank you from me too. this helped me a lot

question about the code above for WriteVariable function

I'm trying to write/update an existing variable defined for the scope of a package.
When I execute the Script Task in SSIS using your above code, I can't get the write changes to stick for the variable "AcreFolder". and AcreFolder is NOT set to read only.

From your code above I derived the following:
Note: "AcreFolder" is a user defined string variable that's initially set to "test". and I want to set it to the string value inside FolderPath. Everything run perfect except updating the "AcreFolder" variable.
If you could help me out that'd be great!!!

Thanks

Vince

Dim vars As Variables
Dim FolderPath As String

FolderPath = "C:\"

Try
Dts.VariableDispenser.LockForWrite("AcreFolder")
Dts.VariableDispenser.GetVariables(vars)
MsgBox(vars(0).ReadOnly)
MsgBox(vars(0).Value)

Try
vars(0).Value = FolderPath
MsgBox(vars(0).Value)

Catch ex As Exception
MsgBox("Unsucessful variable write")


End Try

Catch ex As Exception

MsgBox("Unsucessful Variable access")

End Try

Not Sure What the Problem Could Be...

Hi Vince,

Your code looks OK. Comparing it to my WriteVariable() function, I can see a difference in the fact that it's using vars(0) instead of using the variable name. Shouldn't make a difference, though, especially if your MsgBox is showing you the value you expect.

Have you tried using my WriteVariable() function as-is rather than adapting it? You can paste it into your script and then just call it...

I also notice that your code is not calling vars.Unlock(), which could cause some kind of issue...not sure about that, though.

Hope that helps,
Dan

Tried your WriteVariable function

Hey Dan,
It's wierd, I tried using your function with my script task editor and still the edited changes I made to the variable don't apply after the package is compiled.

Also when I run the package it stalls, when I edit the properties for script task editor, specifically the "ReadWriteVariables" under script. So if I try to add in a variable under that heading, it doesn't successfully execute the script, but no error msg, just stalls. Only executes when no variables are in the heading "ReadWriteVariables" Has this happened to you?

Thanks again,
Vince

Not Sure...

Sorry, Vince, I don't know what to tell you. I've never experienced either of the problems you're describing.

You should not need to have a variable in the ReadWriteVariables if you're using the ReadVariable() and WriteVariable() functions to access it.

Is the variable you're working with defined at package-level scope? I've noticed that it's easy in BIDS to accidentally create a variable at the scope of a lower level container and not realize it.

Sorry I couldn't be more help.

Dan

Need Help with Variables....

I am trying to validate a flat file and insert the values in the SQL Table...It gets tricky here, If while validating, even one record has invalid field I have to abort the package that means no updates or inserts. If everything validates right I have to update the existing record and insert the new ones...
Problem I am running into is I thought I will declare a local variable count the # of errors everytime an error is raised and then decide what to do.As its a local variable my logic will not work as this variable gets initialized for every row.When I try to use the Package variables as I am using a Script component, I get the Read Write Lock error. I cannot use ur functions as they r for scripttask..Any suggestions from u on this...I have spent more than a week researching this...

Thanks

Thank you. I spent hours trying to figure out why I couldn't update a ReadWrite variable. I still don't know why it won't work, but your solution worked perfectly.

Thanks

Get variable names?

Just curious if any of you know of any way to retrieve the names of the user variables which have been setup in a package (via VariableDispenser or any other object).

We have a custom flow task, in which we want to read through each variable that the user has created looking for values with a specific pattern (UNC paths). Considering we do not know which the variables have been named, we have no way of locking them for read.

After a few days of research, we don't think there is a way, but it's hard for me to settle on that answer.

Doh. Got it...

As soon as I posted this I found the answer here.

Awesome!

Thank you.

Just so I understand

Nice blog post first of all.

I am new to SSIS, is what you are saying that I have to copy these functions to each script task? Is there no global scripting library that all script task have visibility to?

SSIS Scripting Shortcomings

imFrustrated wrote: "Is there no global scripting library that all script task have visibility to?"

Not as far as I can tell, there is not, no. The scripting integration at both design time and runtime is pretty clunky. You will do a lot of copying and pasting. When I was writing a lot of code in SSIS I had a I hope this is one area they will be able to improve in future releases.

Dan

P.S.
See also this post for another example of what I mean by "clunky."

ssis vars in scripts

This stuff is a life-saver. I could use your
Dts.VariableDispenser... for a scripts task, but could not for a script component which was reading a table. In your code above (both for read & write) I had to change the "DTs" to "Me", then variable collection was changed also. Here's the variantions on routines:

   Private Function ReadVariable(ByVal varName As String) As Object
Dim result As Object
Try
Dim vars As IDTSVariables90
Me.VariableDispenser.LockForRead(varName)
Me.VariableDispenser.GetVariables(vars)
Try
result = vars(varName).Value
Catch ex As Exception
Throw ex
Finally
vars.Unlock()
End Try
Catch ex As Exception
Throw ex
End Try
Return result
End Function

Private Sub WriteVariable(ByVal varName As String, ByVal varValue As Object)
Try
Dim vars As IDTSVariables90
Me.VariableDispenser.LockForWrite(varName)
Me.VariableDispenser.GetVariables(vars)
Try
vars(varName).Value = varValue
Catch ex As Exception
Throw ex
Finally
vars.Unlock()
End Try
Catch ex As Exception
Throw ex
End Try
End Sub

This is probably not the way to go, but maybe you can improve it. Thanks much

Variable naming in your procedure

Everything I read says you have to refer to variables by their scope, such as 'User::DirectoryIncoming' or 'System::Something'. In your example above you do not use the namespace qualifier. Why? Is it not necessary?

Much appreceiated

I have struggled for a week with the 'Can't get lock on variable' error meesage. I used your example and it all works fine. Please send this to MS and tell them to include it in their documentation.

Thanks again.

Slight variation when in Script Components

Hi Daniel:
As the guest poster on 3/31/07 reported, I also found I had to modify the syntax slightly when using these functions in a data flow Script Component (these functions work perfectly in control flow Script Tasks). Don't know why there's a difference, but the "vars" object variable has to be declared as:
Dim vars As IDTSVariables90
And the VariableDispenser appears to be a static class owned by the Script Component. The "Dts" namespace causes an error, so the following works fine:
VariableDispenser.LockForRead(varName)

BTW: One big reason for using these functions is because SSIS seems to mangle variable names passed to Script Components. When using the ReadOnlyVariables property of the Script Component, my variables (ImportFile_Name and ImportFile_Path) are missing their underscores within the Script Component's code (ImportFileName and ImportFilePath). This made it pretty hard to use the "Me.Variables." as documented in online help.

IMO, it's much cleaner and symmetric to use your functions for reading and writing variables, rather than relying on the ReadOnlyVariables and ReadWriteVariables properties. Code is much easier to read and understand when using your functions.

Cannot write to package level variable

Hi Dan,
I still cannot write to a package level variable in a Script Task. I have 2 variables to create a SQL statement.

  Dim LastRecord As String, sSQL As String
LastRecord = CType(ReadVariable("LastRow"), String)
sSQL = "SELECT * FROM MyTable WHERE RecID>" & LastRecord
WriteVariable("SQL", sSQL)
sSQL = CType(RdVar("SQL"), String)
MsgBox(sSQL)

I'm able to read the value of LastRecord but when I appended to sSQL and call WriteVariable, the MsgBox () shows only the initial value of the Package variable (SQL). Apparently it did not save it... any help???
Thanks in advance

Not Sure...

Sorry, dave, but nothing jumps out at me other than the use of "RdVar" vs. "ReadVariable"--but that may have just been shorthand you're using for your example.

One possibility does come to mind: is it possible that you've also declared the "SQL" variable as read only in the Script Task dialog box? That might cause the behavior you describe. When using ReadVariable() and WriteVariable() you do not need to (and probably should not) declare the variables on the Script Task "Edit" dialog as available to the script.

Hope that helps,
Dan

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Recent comments

User login

About our advertising.

Atom Feed

developer.* Blogs also has an Atom feed, located at this url.

Click here for more information about Atom.

A Jolt Award Finalist
Software Creativity 2.0
Foreword by Tom DeMarco

Recent Posters

Based on most recent 60 days, sorted by # of posts and name.

Google
Web developer.*

Who's online

There are currently 0 users and 31 guests online.

Syndicate

Syndicate content
All views expressed by authors, bloggers, and commentors are their own and do not necessarily reflect the views of developer.* or its proprietors.
Click to read the Copyright Notice.

All content copyright ©2000-2005 by the individual specified authors (and where not specified, copyright by Read Media, LLC). Reprint or redistribute only with written permission from the author and/or developer.*.

www.developerdotstar.com