Change SSIS variable with reference in C#? - c#

I access a read write a "SSIS variable" with C# in script task (Inside SSIS, an ETL tool. Don't worry about the SSIS part. Lets look at the C# only). Its Dts.Variables["strRope"].Value = "onFire"; Is there any way I could refer
to a SSIS variable without using this big name ? I was thinking of -
Object var = Dts.Variables["strRope"].REFERENCE_TO_VARIABLE;
Now, if I want assign a new value to Dts.Variables["strRope"], can I simply say var = (String) "Ten thousand thundering typhoons"; .
Is there any way I can do such a thing ?
EDIT Code example -
public void Main()
{
object var = Dts.Variables["strRope"].Value;
MessageBox.Show("original value = " + Dts.Variables["strRope"].Value.ToString());//original value = "Hello World"
//Try to change the value of Dts.Variables["strRope"].Value using var ???
var = (object)"Hello cruel world !";
MessageBox.Show("new value = " + Dts.Variables["strRope"].Value.ToString());//new value = ???
Dts.TaskResult = (int)ScriptResults.Success;
}

Note that var is a C# 3.0 (and above) keyword, and you can use it instead of object to save on typing and to avoid explicitly casting the type. Try using it along the following lines,
var strRope = Dts.Variables["strRope"];
if (strRope.Value == "onFire") { ... }
strRope.Value = "Ten thousand thundering typhoons";

Related

How execute/run code from string variable at runtime

I am trying to execute code that's in a string variable to get an item from a dictionary
I have tried using CSharpCodeProvider like this:
var text = "IconDescription";
text = "\"" + text + "\"";
var field = "Outcome[" + text + "].Value";
field = "\"" + field + "\"";
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler()
parameters.GenerateExecutable = true;
CompilerResults results = icc.CompileAssemblyFromSource(parameters, field)
var dataTest = JsonConvert.DeserializeObject<DictionaryOutcome.Rootobject>(jsonText);
var t = new List<Outcome>();
var defaultOutcome = dataTest.Response.Outcome.KeyValueOfstringOutcomepQnxSKQu.Select(item => new Outcome
{
DataType = item.Value.DataType,
Value = item.Value.Value1,
Field = item.Key
}).ToList();
defaultOutcome.ToList().ForEach(item =>
{
t.Add(item);
});
the field variable's value is Outcome["IconDescription"].Value, and I want to execute this code to get the value from the Outcome dictionary, using the "IconDescription" Key, and get the value.
Is this possible?
I have tried the following:
var scriptOptions = ScriptOptions.Default
.WithReferences(typeof(Dictionary<string, Outcome>).FullName)
.WithImports("Outcome");
var scripts = CSharpScript.Create<object>(field, scriptOptions);
var resultsx = scripts.RunAsync(null, CancellationToken.None).Result;
And I am getting this error:
error CS0246: The type or namespace name 'Outcome' could not be found (are you missing a using directive or an assembly reference?)
I am struggling to even guess what you are trying to do, but as a starter, consider what you are actually doing by trying to compile that string you are constructing.
Outcome["SomeValue"].Value is not even close to being valid C# code:
it has no scope
it has no entry point
it imports no namespaces
it isn't terminated with a ;
the symbol Outcome is not defined
You're compiling this into an executable, so I don't see how it could have any knowledge of the list of results brought back from deserializing the JSON content, where you haven't specified where you're getting this from.
You haven't specified any evidence that explains why you need such an elaborate solution to merely extract some values from JSON, so a straightforward solution might be to use the built-in features of Newtonsoft.Json:
dataTest[0] selects the first element in the array, when the json object is an array;
dataTest[0]["Outcome"] selects the property Outcome of the first object, which may itself be an object
dataTest[0]["Outcome"]["Value"] selects the property Value of Outcome
All of the string indexes here can be known only at runtime and held in variables. I don't see why you need to do any scripting at all to do this.

My C# JavaScriptSerializer output is empty?

I'm pulling a result set from SQL server into a C# object, where I then use the JavaScriptSerializer to convert it to a json string and output it to a file. The file is created but it only contains
{}
I'm not sure what's going on. I know the SQL query returns data in SSMS as I expect (just that it's too large to display the full output in SSMS). In SSIS where I'm doing the querying and script tasks it seems the result set is empty.....
Here is my Script task:
public void Main()
{
// TODO: Add your code here
JavaScriptSerializer js = new JavaScriptSerializer();
var myJSON = Dts.Variables["User::JSON"];
string json = js.Serialize(myJSON);
//string myJSON = (Dts.Variables["User::JSON"].Value).ToString();
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(#"C:\Users\myUser\Documents\TEST.JSON", true))
{
file.WriteLine(json);
}
Dts.TaskResult = (int)ScriptResults.Success;
}
edits
To add more context my query result that I'm reading into my Script task is such:
Select x,y,z from [table] wher x = 123
FOR JSON AUTO
This does return a json formatted text string in SSMS.
IN SSIS I've set teh query to return 'Full Result Set' and store it in an object variable called 'JSON'.
I've tried setting this variable to type string, which returns an error that cannot convert object type to string.
https://learn.microsoft.com/en-us/sql/relational-databases/json/format-query-results-as-json-with-for-json-sql-server?view=sql-server-2017
Use newtonsoft available via nuget.
Example:
Product product = new Product();
product.Name = "Apple";
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = Newtonsoft.Json.JsonConvert.SerializeObject(product);
{
"Name": "Apple",
"Sizes": [ "Small","Medium","Large"]
}
Maybe try this pseudo code:
//Just make sure that Dts.Variables["User::JSON"] actually returns a valid object.
string output = Newtonsoft.Json.JsonConvert.SerializeObject(Dts.Variables["User::JSON"]);
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\Users\myUser\Documents\TEST.JSON", true))
{
file.WriteLine(json);
}
Dts.TaskResult = (int)ScriptResults.Success;
FINALLY!
variable JSON must be set to object
The query result should be set to Full result set
Before running a script task I had to run it through a foreach loop using
'ForEach ADO Enumerator' on the first table
Under Variable Mapping assign this out to a new variable of type object.
Now in my script task all I need to do is:
string output = Dts.Variables[#"User::JSON"].Value.ToString();
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\Users\myUser\Documents\TEST.JSON", true))
{
file.WriteLine(output);
}
Not sure why i have to read out the object and assign it to another variable in the foreach container but that is the key to my troubles.
This lead me to my solution:
Assigning value from single row result set in ssis giving error in SSIS 2012
http://www.rad.pasfu.com/index.php?/archives/18-Foreach-Loop-based-on-Variable-SSIS.html

How would you match a record from a select statement against a string in c#?

I'm trying to create a statement to check data from a foxpro database against a string in c#
however I can't seem to get it working, would using parameterised queries here help to achieve what I'm trying to do?
string PROPCODE = "IMPORT_" + ID;
string leadtenant = clcodet;
using (OleDbCommand tenantpopulation = new OleDbCommand(#"SELECT
CLCODE,
CLCODEDESC
FROM CLIENT WHERE PROPCODET = " + PROPCODE, importConnection))
{
string tenants = "";
if (#"CLCODE" = leadtenant)
{
if (tenants != String.Empty)
{
//do something
}
}
}
To Clarify, i want to check whether CLCODE, called from tenantpopulation, matches leadtenant, defined elsewhere in the code
As others already noted using parameters is the way to go (not only in VFP but any SQL database). They are not only for preventing SQL injection attacks, using parameters the drivers take care of converting into correct string, adding/removing braces, quotes etc.
string PROPCODE = "IMPORT_" + ID;
string leadtenant = clcodet;
using (OleDbCommand tenantpopulation = new OleDbCommand(#"SELECT
CLCODE
FROM CLIENT WHERE PROPCODET = ?", importConnection))
{
tenantpopulation.Parameters.AddWithValue("p", PROPCODE);
// rest of code seem to be meaningless
// and I didn't see any code where you run your query
// depending on your requirement, I assume PROPCODET is a primary key?
// if so then you to do the check you only need to return the CLCODE
// with ExecuteScalar:
importConnection.Open();
var clcode = (string)tenantpopulation.ExecuteScalar();
importConnection.Close();
string tenants = "";
// in C# equality check is done with == NOT = (assingment)
if (clcode == leadtenant)
{
// tenants is always String.Empty here
if (tenants != String.Empty)
{
//do something
}
}
}
PS: Have you ever thought, using Tom Brother's LinqToVFP from codeplex? With Linq, you don't need to know these SQL dialects much and instead you use Object query (and intellisense).

How to generate initialization of class fields with Roslyn

I know how to create a local variable inside a method, for example this:
LocalDeclarationStatement(VariableDeclaration(IdentifierName("MyClass"))
.WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier("nameOfvariable"))
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
.WithNewKeyword(Token(SyntaxKind.NewKeyword)))))));
would give me:
MyClass nameOfvariable = new MyClass();
But say that I already created a field and now I simply want to initialize it (in a method, constructor or anything) like this:
nameOfVariable = new MyClass();
How do I do this? My guess it have to do with the VariableDeclerator but I can't find a way to get it right so I can add it to a list that contains StatementSyntaxes. I can change the VariableDecleration to "VariableDeclaration(IdentifierName(""))" too but that gives me an ugly extra space infront of the statement.
It seems like I struggle with some really basic stuff of Roslyn and I try to check http://roslynquoter.azurewebsites.net/ but that feels like the forced way to do it (feels like it create a lot more code than necessary).
Update: Should clarify that I know how to create method/constructors. I'm only looking for a way to initialize a field when I only have access to the field name and field type. So the only code I want to generate is this:
myField = new MyField();
Well you're almost there, you just need to create all that. This should do what you're interested in:
const string source = #"
using System;
class MyClass
{
void Method()
{
MyClass nameOfVariable;
}
}
";
var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
var local = root.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().First();
var declaration = local.Declaration;
var declarator = declaration.Variables.First();
var identifier = SyntaxFactory.IdentifierName("MyClass");
var objectCreationExpression = SyntaxFactory.ObjectCreationExpression(identifier, SyntaxFactory.ArgumentList(), null);
var equalsValueClause = SyntaxFactory.EqualsValueClause(objectCreationExpression);
var newDeclarator = declarator.WithInitializer(equalsValueClause).WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(declarator, newDeclarator);
var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, new AdhocWorkspace());
Console.WriteLine(formattedRoot.GetText());
Console.Read();
Some explanation: you create a new identifier MyClass which will be used in your ObjectCreationExpression. Then you wrap all that in an EqualsValueClause and you set that as an initializer to your declarator. We also add the Formatter annotation to this node so we can format it later and don't end up with whitespace issues.
All that's left then is replacing the node in your original tree, formatting it and you're done:
-------------------------------------------------------------------------------
If you instead mean that you want to put the assignment on its own separately from the declaration then you have to create a new AssignmentExpression and wrap it inside a ExpressionStatement. Typically expressions and statements are distinct concepts but this ExpressionStatement allows us to treat an expression as a statement which is important because a method's body only accepts statements.
In code, it looks like this:
internal static void Execute()
{
const string source = #"
using System;
class MyClass
{
void Method()
{
MyClass nameOfVariable, another;
}
}
";
var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
var local = root.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().First();
var method = local.Ancestors().OfType<MethodDeclarationSyntax>().First();
var variableIdentifier = SyntaxFactory.IdentifierName("nameOfVariable");
var classIdentifier = SyntaxFactory.IdentifierName("MyClass");
var objectCreationExpression = SyntaxFactory.ObjectCreationExpression(classIdentifier, SyntaxFactory.ArgumentList(), null);
var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, variableIdentifier, objectCreationExpression);
var expressionStatement = SyntaxFactory.ExpressionStatement(assignment).WithAdditionalAnnotations(Formatter.Annotation);
var newMethod = method.AddBodyStatements(expressionStatement);
var newRoot = root.ReplaceNode(method.Body, newMethod.Body);
var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, new AdhocWorkspace());
Console.WriteLine(formattedRoot.GetText());
Console.Read();
}
Result:
After some more trying and looking I found the answer. There is something called "AssignmentExpression" that you can use.
Here is an example how to use it:
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("myField"),
ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
.WithNewKeyword(Token(SyntaxKind.NewKeyword))));
This would give you:
myField = new Myclass();
So now it's easy to seperate creation and assignment/initialization to two different statements.
Note that I'm using "using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;" so I don't have to write SyntaxFactory all the time.
Or you can goto "http://roslynquoter.azurewebsites.net/" and paste your code in the small little textbox and click "Get Roslyn API calls to generate this code".
(I can generate the code you posted above, but it is kinda long. so i use a simple example.
For example, let's say you paste "DateTime mydate2 = new DateTime()", the tool will generate the following code :-
LocalDeclarationStatement(
VariableDeclaration(
IdentifierName("DateTime"))
.WithVariables(
SingletonSeparatedList<VariableDeclaratorSyntax>(
VariableDeclarator(
Identifier("mydate2"))
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(
IdentifierName("DateTime"))
.WithArgumentList(
ArgumentList())))))).WithSemicolonToken(
MissingToken(SyntaxKind.SemicolonToken)).NormalizeWhitespace()
Then you just have to fix up the code using SyntaxFactory, for example :-
var myDeclaratyion = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(
SyntaxFactory.IdentifierName("DateTime")).
WithVariables(
SyntaxFactory.SingletonSeparatedList<VariableDeclaratorSyntax>(
SyntaxFactory.VariableDeclarator(
SyntaxFactory.Identifier("mydate2")).
WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(
SyntaxFactory.IdentifierName("DateTime"))
.WithArgumentList(
SyntaxFactory.ArgumentList())))))).WithSemicolonToken(SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken)).NormalizeWhitespace();

List<> of new, on-the-fly object

I've got a bit of code that is giving me some trouble.
I'm trying to bundle a list of images into a zip file. The problem I'm having is that, on occasion, one of the images will be opened when it is accessed, causing an exception.
I'm pretty sure it's a timing issue, so I'm coding a 'second chance' loop to catch the images that fall through (as opposed to the existing behavior, where it halts on any error and gives back what it has thusfar).
I have the potentially erroring sections of code in a try block, as seen below
if (!Directory.Exists(physicalPath + "/" + fi.Description))
{
Directory.CreateDirectory(physicalPath + "/" + fi.Description);
}
wc.DownloadFile(source, physicalPath + "/" + fileName);
ze = zip.AddFile(physicalPath + "/" + fileName, path);
ze.FileName = fileName;
'ze' is a 'ZipEntry' from the Ionic.Zip library, and 'wc' is a WebClient.
In my catch, I need to store two pieces of information: 'source' and the string that results from 'physicalPath + "/" + filename'.
I know there's a way in .NET 4 to dynamically create a new object to hold this data, but I don't recall what it's called. This has greatly hampered my google-fu.
How can I create a dynamic object that will hold a pair of strings (preferably with property names on the variables) without creating a whole new class?
Are you referring to a Tuple?
http://msdn.microsoft.com/en-us/library/system.tuple.aspx
You can create an anonymous type like this:
var obj = new { str1 = "something", str2 = "something else" };
Console.WriteLine(string.Format("str1: {0}, str2: {1}", obj.str1, obj.str2));
Edit #1 To make an array of the anonymous type ...
var arr = new[] {
new { str1 = "a", str2 = "b" },
new { str1 = "c", str2 = "c" },
};
Edit #2 To make a Generic List out of this, you have to get a bit fancier ...
var obj = new { str1 = "", str2 = "" };
var type = typeof(List<>);
var listType = t.MakeGenericType(obj.GetType());
var list = (IList)Activator.CreateInstance(listType);
list.Add(new { str1 = "something", str2 = "something else" });
Console.WriteLine(((dynamic)list[0]).str1);
or a dynamic object? http://msdn.microsoft.com/en-us/library/dd264736.aspx
How can I create a dynamic object that will hold a pair of strings (preferably with property names on the variables) without creating a whole new class?
Use a Tuple.
// this uses a tuple to create a generic class that holds two string values.
// We also use string.Format instead of path + "/" + fileName to generate the second value.
var zipData = new Tuple<string, string>(source, string.Format("{0}/{1}", physicalPath, fileName));

Categories