I'm trying to set variables in code on an SSIS package I'm kicking off from C#.
Package pkg = app.LoadPackage(ConfigurationManager.AppSettings["SsisPkg"].ToString(), null);
pkg.Variables["User::FolderPath"].Value = Path.GetDirectoryName(e.FullPath);
pkg.Variables["User::FileName"].Value = Path.GetFileNameWithoutExtension(e.FullPath);
While debugging, I inspect the values directly after setting them, but nothing has changed. I can drill into the value (hover, navigate to it, edit value) so it doesn't seem to be that they aren't editable.
Any ideas what I'm doing wrong here?
Update: Thanks to billinkc, I'm comfortable what I'm doing should work so long as possibly other conditions are met. I've found that direct assignments in code fail (no error, just don't "take") and I cannot edit the values in the watch window. I can edit values when I'm inspecting them.
Could the actual issue be that there's a setting flagging these as read-only?
Answer Found: Needed to make the variables writable before setting the value:
pkg.Variables["User::FileName"].EvaluateAsExpression = false;
You're doing it right. I created a basic SSIS package. Has one Variable, FolderPath, type string. There is a single script task that fires an Information event and the contents of that expose the value of the FolderPath variable
I then created a basic C# console app like this
public class InformationListener : DefaultEvents
{
public override void OnInformation(DtsObject source, int informationCode, string subComponent, string description, string helpFile, int helpContext, string idofInterfaceWithError, ref bool fireAgain)
{
//base.OnInformation(source, informationCode, subComponent, description, helpFile, helpContext, idofInterfaceWithError, ref fireAgain);
Console.WriteLine(string.Format("{0} {1}", subComponent, description));
}
}
class Program
{
static void Main(string[] args)
{
string sourcePackage = string.Empty;
string path = string.Empty;
string variableName = string.Empty;
string designValue = string.Empty;
string newValue = string.Empty;
InformationListener listener = null;
sourcePackage = #"J:\Src\SO\SSIS\Package.dtsx";
path = #"J:\runtime";
variableName = "User::FolderPath";
listener = new InformationListener();
Application app = new Application();
Package pkg = null;
Variable ssisVariable = null;
pkg = app.LoadPackage(sourcePackage, null);
ssisVariable = pkg.Variables[variableName];
designValue = ssisVariable.Value.ToString();
Console.WriteLine(string.Format("Designtime value = {0}", designValue));
ssisVariable.Value = path;
newValue = ssisVariable.Value.ToString();
Console.WriteLine(string.Format("new value = {0}", newValue));
DTSExecResult results = DTSExecResult.Canceled;
results = pkg.Execute(null, null, listener, null, null);
Console.WriteLine("Press any key to continue");
Console.ReadKey();
}
}
As you can tell from the variable inspection
and from my print statements
the design-time value of C:\designTime goes to J: because I forgot to escape my string above but we can pretend it shows J:\runtime.
All this said, unless we serialize the package with a call to the SaveToXml method, the value of User::FolderPath resets to the design time value once the object goes out of scope. Permanently updating would look like
app.SaveToXml(sourcePackage, pkg, null);
OP EDIT this discussion and examples led me to the answer:
http://social.msdn.microsoft.com/Forums/sqlserver/en-US/dad8e218-1fe0-49db-89da-5715fb6d4b21/sql-2008-r2-ssis-c-script-task-not-setting-variable
Related
I must use a text file "db.txt" which inherits the names of the Server and Database to make my connection string complete.
db.txt looks like this:
<Anfang>
SERVER==dbServer\SQLEXPRESS
DATABASE==studentweb
<Ende>
The connection string:
string constr = ConfigurationManager.ConnectionStrings["DRIVER={SQL Server}; SERVER=SERVER DATABASE=DB UID=;PWD=;LANGUAGE=Deutsch;Trusted_Connection=YES"].ConnectionString;
Unfortunatly we are only allowed to use Classic ASPX.net (C# 2.0) and not the web.config.
I've searched a lot, but found nothing close to help me.
Somebody got an Idea how to make it work?
Here is something to get you going.
In a nutshell, I put the DBInfo file through a method that reads the file line by line. When I see the line <anfang> I know the next line will be important, and when I see the line <ende> I know it's the end, so I need to grab everything in between. Hence why I came up with the booleans areWeThereYet and isItDoneYet which I use to start and stop gathering data from the file.
In this snippet I use a Dictionary<string, string> to store and return the values but, you could use something different. At first I was going to create a custom class that would hold all the DB information but, since this is a school assignment, we'll go step by step and start by using what's already available.
using System;
using System.Collections.Generic;
namespace _41167195
{
class Program
{
static void Main(string[] args)
{
string pathToDBINfoFile = #"M:\StackOverflowQuestionsAndAnswers\41167195\41167195\sample\DBInfo.txt";//the path to the file holding the info
Dictionary<string, string> connStringValues = DoIt(pathToDBINfoFile);//Get the values from the file using a method that returns a dictionary
string serverValue = connStringValues["SERVER"];//just for you to see what the results are
string dbValue = connStringValues["DATABASE"];//just for you to see what the results are
//Now you can adjust the line below using the stuff you got from above.
//string constr = ConfigurationManager.ConnectionStrings["DRIVER={SQL Server}; SERVER=SERVER DATABASE=DB UID=;PWD=;LANGUAGE=Deutsch;Trusted_Connection=YES"].ConnectionString;
}
private static Dictionary<string, string> DoIt(string incomingDBInfoPath)
{
Dictionary<string, string> retVal = new Dictionary<string, string>();//initialize a dictionary, this will be our return value
using (System.IO.StreamReader sr = new System.IO.StreamReader(incomingDBInfoPath))
{
string currentLine = string.Empty;
bool areWeThereYet = false;
bool isItDoneYet = false;
while ((currentLine = sr.ReadLine()) != null)//while there is something to read
{
if (currentLine.ToLower() == "<anfang>")
{
areWeThereYet = true;
continue;//force the while to go into the next iteration
}
else if (currentLine.ToLower() == "<ende>")
{
isItDoneYet = true;
}
if (areWeThereYet && !isItDoneYet)
{
string[] bleh = currentLine.Split(new string[] { "==" }, StringSplitOptions.RemoveEmptyEntries);
retVal.Add(bleh[0], bleh[1]);//add the value to the dictionary
}
else if (isItDoneYet)
{
break;//we are done, get out of here
}
else
{
continue;//we don't need this line
}
}
}
return retVal;
}
}
}
I am fairly new to c# and am getting an error "Object reference not set to an instance of an object." I am creating an XML packet and sending it to an external device for control. If I put the following code on the form in a click event it works beautifully.
On the btn Click event it looks like this:
SetTestInfoResponse testDataDs = null;
TestInformation testInfo = null;
this.PopulateTestDataXml();
string stringRequestXML = string.Empty;
string stringResponseXML = string.Empty;
//Creates Request packet
stringRequestXML = XMLCommunicationPackets.SetTestInformation (testInfo, testInfo.TestID, testInfo.TestUser, testInfo.TestSampleType, testInfo.TestSampleId, testInfo.TestMethodNumber, testInfo.TestTubeSn, testInfo.TestComments);
//Write set Test Info XML Packet and get response for ack or failure.
stringResponseXML = PluginContext.GetInstance().InstrumentDriverCurrent.GetInstrumentControl().SetCommonParameter(stringRequestXML);
However, If I move my entire function out of the form and try to call it when clicking a button I get the error.
written in a method off the form in a .cs file it reads:
public static SetTestInfoResponse SetTestData()
{
SetTestInfoResponse testDataDs = null;
TestInformation testInfo = null;
string stringRequestXML = string.Empty;
string stringResponseXML = string.Empty;
//Creates Request packet
stringRequestXML = XMLCommunicationPackets.SetTestInformation (testInfo, testInfo.TestID, testInfo.TestUser, testInfo.TestSampleType, testInfo.TestSampleId, testInfo.TestMethodNumber, testInfo.TestTubeSn, testInfo.TestComments);
//Write set Test Info XML Packet and get response for ack or failure.
stringResponseXML = PluginContext.GetInstance().InstrumentDriverCurrent.GetInstrumentControl().SetCommonParameter(stringRequestXML);
The error occurs when building stringRequestXml.
Part of my problem is the PopulateTestData() is a method on the form itself. Its purpose is to take data from txtboxes and cmbboxes and assign them to their respective arguments..
private TestInformation PopulateTestDataXml()
{
TestInformation UiTestData = new TestInformation();
UiTestData.TestID = txtTestId.Text;
UiTestData.TestUser = cmbUsers.SelectedItem.ToString();
UiTestData.TestSampleType = txtSampleType.Text;
UiTestData.TestSampleId = txtSampleId.Text;
UiTestData.TestMethodNumber = Convert.ToInt32(cmbMethod.SelectedItem);
UiTestData.TestTubeSn = txtTubeSerialNum.Text;
UiTestData.TestComments = txtComments.Text;
return UiTestData;
}
Here is the SetTestInformation() method where I am getting the error:
public static string SetTestInformation(TestInformation testInfo, string stringTestId, string stringUser, string stringSampleType, string stringSampleId, int intMethodNumber, string stringTubeSn, string stringComments)
{
try
{
string stringRequestXMLPacket = string.Empty;
string stringType = #"Request";
string stringCommand = #"Set";
string stringArgument = #"TestInformation";
CommunicationPacket requestXMLPacket = new CommunicationPacket(stringRootTag, stringXMLVersion, stringType, stringCommand);
requestXMLPacket.AddCommandArgument(stringArgument);
requestXMLPacket.AddArgumentItem(stringArgument, "sTestId", testInfo.TestID.ToString());
requestXMLPacket.AddArgumentItem(stringArgument, "sUser", testInfo.TestUser.ToString());
requestXMLPacket.AddArgumentItem(stringArgument, "sSampleType", testInfo.TestSampleType.ToString());
requestXMLPacket.AddArgumentItem(stringArgument, "sSampleId", testInfo.TestSampleId.ToString());
requestXMLPacket.AddArgumentItem(stringArgument, "nMethodNumber", testInfo.TestMethodNumber.ToString());
requestXMLPacket.AddArgumentItem(stringArgument, "sTubeSn", testInfo.TestTubeSn.ToString());
requestXMLPacket.AddArgumentItem(stringArgument, "sComments", testInfo.TestComments.ToString());
stringRequestXMLPacket = requestXMLPacket.CreateXMLPacket();
return stringRequestXMLPacket;
}
catch (Exception ex)
{
throw ex;
}
}
Iknow I am having trouble with the variable scope here. I still have to use the method PopulateTestDataXml on the form before I call the setTestData() method. But when I call the Method I have to declare testInfo = null or the parameters for SetTestInformation are not valid ("Does not exist in the current context"). What would I need to pass and how for this to work as a called method on the form btn click? I need to do this as I have alot of deserializing functions written as well to catch error messages in the response xml (these all work fine) and its just too much info on the click event. (And I need to learn).
Thanks
Neither of your examples should work (regardless of where you put them). This is simply incorrect:
TestInformation testInfo = null;
// ...
stringRequestXML = XMLCommunicationPackets.SetTestInformation (testInfo,
testInfo.TestID, ...);
// ^^ BANG!
Your testInfo object is null. When you try and access anything on a null object.. a NullReferenceException is thrown. You need to initialize it first. You're trying to do that in your PopulateTestDataXml method.. which returns the object your after. So change your code to this:
TestInformation testInfo = PopulateTestDataXml(); // assign it
Here is your problem..
public static SetTestInfoResponse SetTestData()
{
SetTestInfoResponse testDataDs = null;
TestInformation testInfo = null;
string stringRequestXML = string.Empty;
string stringResponseXML = string.Empty;
//Creates Request packet
stringRequestXML = XMLCommunicationPackets.SetTestInformation (testInfo, testInfo.TestID, testInfo.TestUser, testInfo.TestSampleType, testInfo.TestSampleId, testInfo.TestMethodNumber, testInfo.TestTubeSn, testInfo.TestComments);
//Write set Test Info XML Packet and get response for ack or failure.
stringResponseXML = PluginContext.GetInstance().InstrumentDriverCurrent.GetInstrumentControl().SetCommonParameter(stringRequestXML);
Are you assigning values for these objects I see they are just declared but never assigned.
SetTestInfoResponse testDataDs = null;
TestInformation testInfo = null;
i don't see you use null objects, so i'm wonder if you set them later, also u said the error happen on
private TestInformation PopulateTestDataXml()
{
TestInformation UiTestData = new TestInformation();
UiTestData.TestID = txtTestId.Text;
UiTestData.TestUser = cmbUsers.SelectedItem.ToString();
UiTestData.TestSampleType = txtSampleType.Text;
UiTestData.TestSampleId = txtSampleId.Text;
UiTestData.TestMethodNumber = Convert.ToInt32(cmbMethod.SelectedItem);
UiTestData.TestTubeSn = txtTubeSerialNum.Text;
UiTestData.TestComments = txtComments.Text;
return UiTestData;
}
after moving it out of your form, which mean possibly it's text box references is broken...
so what you can do, is store a pointer, like in your program.cs where you call your form to show up,
you can create an static object of form, and then put it in your class, then set it in program.cs file like :
Form1 f=new Form();
MyClass.staticFormPointer = f;
and also replace (new Form()), with (f) on the calling method,
your my class is like this:
class MyClass{
public static Form1 staticFormPointer = null;
//your code
.
.
.
// and in your methods you call it like this txtBox1.Text -> staticFormPointer.txtBox1.Text
}
All, to provide a on-the-fly mechanism for debugging an application in different languages I am using the required resource string (in a foreign language) to display the English equivalent at run-time should the user require it. This is done using
public static string GetMessage(string messageKey)
{
CultureInfo culture = Thread.CurrentThread.CurrentCulture;
if (!culture.DisplayName.Contains("English"))
{
string fileName = "MessageStrings.resx";
string appDir = Path.GetDirectoryName(Application.ExecutablePath);
fileName = Path.Combine(appDir, fileName);
if (File.Exists(fileName))
{
// Get the English error message.
using (ResXResourceReader resxReader = new ResXResourceReader(fileName))
{
foreach (DictionaryEntry e in resxReader)
if (e.Key.ToString().CompareNoCase(messageKey) == 0)
return e.Value.ToString();
}
}
}
return null;
}
Where GetName is defined as
public static string GetName<T>(Expression<Func<T>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
I usually display localised messages in my application like
Utils.ErrMsg(MessageStrings.SomeMessage);
or
Utils.ErrMsg(String.Format(MessageStrings.SomeMessage, param1, param2));
Now I can display the relevent English message from my app running in a different culture using
Utils.ErrMsg(Utils.GetMessage(
Utils.GetName(() => MessageStrings.ErrCellAllocStatZeroTotal)) ??
MessageStrings.ErrCellAllocStatZeroTotal);
I want to avoid having to use a lambda expression in the call to GetName and the use of null from GetMessage and using ??, how can I achieve this [if at all possible]?
Thanks for your time.
I do not fully understand your code, but if you just want to access the properties of an object dynamically, try this (you have to replace [Object] and "PropertyName" with your specific values):
// get the property from object
PropertyInfo Property = [Object].GetType().GetProperty("PropertyName");
// get the value
int value = (int)Property.GetValue([Object], null);
How do I programmatically locate my Dropbox folder using C#?
* Registry?
* Environment Variable?
* Etc...
UPDATED SOLUTION
Dropbox now provides an info.json file as stated here: https://www.dropbox.com/en/help/4584
If you don't want to deal with parsing the JSON, you can simply use the following solution:
var infoPath = #"Dropbox\info.json";
var jsonPath = Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), infoPath);
if (!File.Exists(jsonPath)) jsonPath = Path.Combine(Environment.GetEnvironmentVariable("AppData"), infoPath);
if (!File.Exists(jsonPath)) throw new Exception("Dropbox could not be found!");
var dropboxPath = File.ReadAllText(jsonPath).Split('\"')[5].Replace(#"\\", #"\");
If you'd like to parse the JSON, you can use the JavaScripSerializer as follows:
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var dictionary = (Dictionary < string, object>) serializer.DeserializeObject(File.ReadAllText(jsonPath));
var dropboxPath = (string) ((Dictionary < string, object> )dictionary["personal"])["path"];
DEPRECATED SOLUTION:
You can read the the dropbox\host.db file. It's a Base64 file located in your AppData\Roaming path. Use this:
var dbPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Dropbox\\host.db");
var dbBase64Text = Convert.FromBase64String(System.IO.File.ReadAllText(dbPath));
var folderPath = System.Text.ASCIIEncoding.ASCII.GetString(dbBase64Text);
Hope it helps!
UPDATE JULY 2016: THE CODE BELOW NO LONGER WORKS DUE TO CHANGES IN THE DROPBOX CLIENT, SEE ACCEPTED ANSWER ABOVE FOR UP-TO-DATE SOLUTION
Reinaldo's answer is essentially correct but it gives some junk output before the path because there seem to be two lines in the host.db file and in this case you only want to read the second one. The following will get you just the path.
string appDataPath = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
string dbPath = System.IO.Path.Combine(appDataPath, "Dropbox\\host.db");
string[] lines = System.IO.File.ReadAllLines(dbPath);
byte[] dbBase64Text = Convert.FromBase64String(lines[1]);
string folderPath = System.Text.ASCIIEncoding.ASCII.GetString(dbBase64Text);
Console.WriteLine(folderPath);
Cleaner version based on previous answers (use var, added exists check, remove warnings):
private static string GetDropBoxPath()
{
var appDataPath = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
var dbPath = Path.Combine(appDataPath, "Dropbox\\host.db");
if (!File.Exists(dbPath))
return null;
var lines = File.ReadAllLines(dbPath);
var dbBase64Text = Convert.FromBase64String(lines[1]);
var folderPath = Encoding.UTF8.GetString(dbBase64Text);
return folderPath;
}
This seems to be the suggested solution from Dropbox: https://www.dropbox.com/help/4584?path=desktop_client_and_web_app
Dropbox has added a new helper, there is a JSON file in either %APPDATA%\Dropbox\info.json or %LOCALAPPDATA%\Dropbox\info.json.
See https://www.dropbox.com/help/4584 for more information.
public static string getDropBoxPath()
{
try
{
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var dbPath = Path.Combine(appDataPath, "Dropbox\\host.db");
if (!File.Exists(dbPath))
{
return null;
}
else
{
var lines = File.ReadAllLines(dbPath);
var dbBase64Text = Convert.FromBase64String(lines[1]);
var folderPath = Encoding.UTF8.GetString(dbBase64Text);
return folderPath;
}
}
catch (Exception ex)
{
throw ex;
}
}
It's not stored in the registry (at least it isn't in plain text). I believe it's stored in the following location.
C:\Users\userprofile\AppData\Roaming\Dropbox
I would say it resides in the host.db or unlink.db file.
The config.db is a sqlite file. The other two are unknown (encrypted). The config.db contains a blob field only with the schema version.
The host.db method has stopped working in later versions of dropbox.
https://www.dropbox.com/en/help/4584 gives the recommended approach.
Here is the c# code I wrote to parse the json and get the dropbox folder.
// https://www.dropbox.com/en/help/4584 says info.json file is in one of two places
string filename = Environment.ExpandEnvironmentVariables( #"%LOCALAPPDATA%\Dropbox\info.json" );
if ( !File.Exists( filename ) ) filename = Environment.ExpandEnvironmentVariables( #"%APPDATA%\Dropbox\info.json" );
JavaScriptSerializer serializer = new JavaScriptSerializer();
// When deserializing a string without specifying a type you get a dictionary <string, object>
Dictionary<string, object> obj = serializer.DeserializeObject( File.ReadAllText( filename ) ) as Dictionary<string, object>;
obj = obj[ "personal" ] as Dictionary<string, object>;
string path = obj[ "path" ] as string;
return path;
I'm posting here a solution that does not use Dictionary; so many years after original answers, every time that I try to use answers from Reinaldo and Derek, I get a Could not load type 'System.Web.Util.Utf16StringValidator' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=... using both LinqPad 7 (.NET 6.0.9) and VS 2022 (Net Standard 2.0),
I do not know if this error is because I'm already referencing Newtonsoft.Json in Assembly as suggested in this unaccepted answer.
Anyway, here is 2022 piece of cake way to do it:
private static string GetDropBoxPath()
{
// https://www.dropbox.com/en/help/4584 says info.json file is in one of two places
string jsonPath = Environment.ExpandEnvironmentVariables(#"%LOCALAPPDATA%\Dropbox\info.json");
if (!File.Exists(jsonPath)) jsonPath = Environment.ExpandEnvironmentVariables(#"%APPDATA%\Dropbox\info.json");
var dropbox = JsonConvert.DeserializeObject<DropboxRoot>(File.ReadAllText(jsonPath));
return dropbox.personal.path;
}
And these are the auxiliary classes:
public class DropboxRoot
{
public Personal personal { get; set; }
}
public class Personal
{
public string path { get; set; }
public long host { get; set; }
public bool is_team { get; set; }
public string subscription_type { get; set; }
}
Considering that the debug data file is available (PDB) and by using either System.Reflection or another similar framework such as Mono.Cecil, how to retrieve programmatically the source file name and the line number where a type or a member of a type is declared.
For example, let's say you have compiled this file into an assembly:
C:\MyProject\Foo.cs
1: public class Foo
2: {
3: public string SayHello()
4: {
5: return "Hello";
6: }
7: }
How to do something like:
MethodInfo methodInfo = typeof(Foo).GetMethod("SayHello");
string sourceFileName = methodInfo.GetSourceFile(); // ?? Does not exist!
int sourceLineNumber = methodInfo.GetLineNumber(); // ?? Does not exist!
sourceFileName would contain "C:\MyProject\Foo.cs" and sourceLineNumber be equal to 3.
Update: System.Diagnostics.StackFrame is indeed able to get that information, but only in the scope of current executing call stack. It means that the method must be invoked first. I would like to get the same info, but without invoking the type member.
Up to date method:
private static void Log(string text,
[CallerFilePath] string file = "",
[CallerMemberName] string member = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, text);
}
New Framework API which populates arguments (marked with special attributes) at runtime,
see more in my answer to this SO question
Using one of the methods explained above, inside the constructor of an attribute, you can provide the source location of everything, that may have an attribute - for instance a class. See the following attribute class:
sealed class ProvideSourceLocation : Attribute
{
public readonly string File;
public readonly string Member;
public readonly int Line;
public ProvideSourceLocation
(
[CallerFilePath] string file = "",
[CallerMemberName] string member = "",
[CallerLineNumber] int line = 0)
{
File = file;
Member = member;
Line = line;
}
public override string ToString() { return File + "(" + Line + "):" + Member; }
}
[ProvideSourceLocation]
class Test
{
...
}
The you can write for instance:
Console.WriteLine(typeof(Test).GetCustomAttribute<ProvideSourceLocation>(true));
Output will be:
a:\develop\HWClassLibrary.cs\src\Tester\Program.cs(65):
you might find some help with these links:
Getting file and line numbers without deploying the PDB files
also found this following post
"Hi Mark,
The following will give you the line number of your code (in the
source file):
Dim CurrentStack As System.Diagnostics.StackTrace
MsgBox (CurrentStack.GetFrame(0).GetFileLineNumber)
In case you're interested, you can find out about the routine that you're
in, as well as all its callers.
Public Function MeAndMyCaller As String
Dim CurrentStack As New System.Diagnostics.StackTrace
Dim Myself As String = CurrentStack.GetFrame(0).GetMethod.Name
Dim MyCaller As String = CurrentStack.GetFrame(1).GetMethod.Name
Return "In " & Myself & vbCrLf & "Called by " & MyCaller
End Function
This can be very handy if you want a generalised error routine because it
can get the name of the caller (which would be where the error occurred).
Regards,
Fergus
MVP [Windows Start button, Shutdown dialogue]
"