Setting a variable value using a Script Task in SSIS - c#

Using Script Task, I am trying to set the value of a variable A the value of variable B. Both in the same package.
This is the code I used:
public void Main() {
Dts.Variables["User::VariableA"].Value = Dts.Variables["User::VariableB"].Value;
Dts.TaskResult = (int)ScriptResults.Success;
}
Both variables are string type, and marked as Read/Write. I tried to convert them to string in the script too using ToString() method, but it didn't work as well.
Anyone knows what did I miss?

You missed nothing. Or perhaps, you've oversimplified the problem for here as a minimal reproduction does not show the same behaviour
I created a package as shown. Two string variables, Variable A and Variable B. They were initialized to "My Value is A" and "My Value is B." I confirm this by printing the current variables to the output window. I then use your code to update the value of A. I then reuse the same inspection code and see the expected final value of "My Value is B" for both variables.
SCR Echo Value
This is the same script task, before and after I update the value. It simply emits the read/write variables to the Information event so they show on the Results/Output tab
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
namespace ST_5e898ddfc3e24549a581a83a3cabab4d
{
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
public void Main()
{
bool fireAgain = false;
foreach (var item in Dts.Variables)
{
Dts.Events.FireInformation(0, "SCR Echo Back", string.Format("{0}->{1}", item.QualifiedName, item.Value), "", 0, ref fireAgain);
}
Dts.TaskResult = (int)ScriptResults.Success;
}
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
}
}
SCR Assign B to A
This is the supplied code
using System;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
namespace ST_3a3b3c73d21c472aba3c2ddbad1481b1
{
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
public void Main()
{
Dts.Variables["User::VariableA"].Value = Dts.Variables["User::VariableB"].Value;
Dts.TaskResult = (int)ScriptResults.Success;
}
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
}
}
What else could it be?
Looking in the wrong place
The Variables window shows the Design-Time value of the package. Perhaps you're thinking that the variable isn't being updated because you're looking at that value.
Instead, if you have a breakpoint or something, look at the Locals window under Variables and that will show what the current Run-Time value is
Express yourself
There's an expression at play. An expression on a variable will always supersede an assigned value. It won't raise an error trying to assign to the Value property but it won't "stick" because the EvalateAsExpression property being true means the Value is always computed based on the Expression property.
In the newer versions of BIDS/SSDT, they make this much more readily apparent with visual cues. The Properties are available via F4 but the Variables window now shows a glyph, f(x), next to the variable name along with pale grey text for the Data Type and Value.

Related

VSIX: IErrorTag tooltip content not displaying

I am trying to write a code analysis extension for visual studio using MEF. I have implemented the ITagger interface for an IErrorTag along with the required ITaggerProvider. As a result, i get the expected squiggles in the editor window for the issues my code analysis finds. However, when hovering above the squiggles with the mouse, the respective tooltip content is never displayed.
Here is a minimalistic example which has the same problem:
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using System;
using System.Collections.Generic;
namespace CodeAnalyzer
{
struct DummyIssue
{
public int Line; // one based line
public string ToolTip;
public DummyIssue(int line, string toolTip)
{
Line = line;
ToolTip = toolTip;
}
}
internal class DummyCodeCheckTagger : ITagger<IErrorTag>
{
readonly List<DummyIssue> mIssues;
readonly ITextView TextView;
public DummyCodeCheckTagger(ITextView textView)
{
TextView = textView;
mIssues = new List<DummyIssue>
{
new DummyIssue(1, "asldfjoqwet"),
new DummyIssue(7, "ASASDAER")
};
textView.LayoutChanged += Update;
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
private void Update(object sender, TextViewLayoutChangedEventArgs args)
{
TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(args.NewSnapshot, 0, args.NewSnapshot.Length)));
}
IEnumerable<ITagSpan<IErrorTag>> ITagger<IErrorTag>.GetTags(NormalizedSnapshotSpanCollection spans)
{
var issues = mIssues;
foreach (var span in spans)
{
foreach (var issue in issues)
{
int zeroBasedLine = issue.Line - 1;
ITextSnapshotLine snapshotLine = TextView.TextSnapshot.GetLineFromLineNumber(zeroBasedLine);
SnapshotSpan snapshotSpan = snapshotLine.Extent;
if (spans.IntersectsWith(snapshotSpan))
{
yield return new TagSpan<IErrorTag>(snapshotSpan, new ErrorTag(PredefinedErrorTypeNames.SyntaxError, issue.ToolTip));
}
}
}
}
}
}
The result looks like this:
tooltip not displaying
What am i missing to get the tooltip displayed?
Fater's comment above led me to think about the problem again. Since i already tried the suggestions in the document posted by fater without success, i started thinking if the problem could be somewhere else.
It turns out that the ITagger implementation was not the problem, but the ITaggerProvider implementation caused the strange behavior. For that,
I pretty much followed the VSIX ErrorList example implementing a SpellChecker, which contains the following code
/// <summary>
/// Create a tagger that does spell checking on the view/buffer combination.
/// </summary>
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
{
ITagger<T> tagger = null;
// Only attempt to spell check on the view's edit buffer (and multiple views could have that buffer open simultaneously so
// only create one instance of the spell checker.
if ((buffer == textView.TextBuffer) && (typeof(T) == typeof(IErrorTag)))
{
var spellChecker = buffer.Properties.GetOrCreateSingletonProperty(typeof(SpellChecker), () => new SpellChecker(this, textView, buffer));
// This is a thin wrapper around the SpellChecker that can be disposed of without shutting down the SpellChecker
// (unless it was the last tagger on the spell checker).
tagger = new SpellCheckerTagger(spellChecker) as ITagger<T>;
}
return tagger;
}
The point is, that the code above only creates an ITagger for a certain view. In that case the created tagger is used only for providing the squiggles in the editor window view. Visual Studio uses a different tagger instance for providing the tooltips for the squiggles and another tagger instance for coloring the scroll bar in the editor window. I had assumed that this would be done by one single tagger instance.

Programatically bind script property on Unity (edit mode not at runtime)

For example, I have MonoBehaviour derived class called Foo:
class Foo : MonoBehaviour {
public Button lastButton;
public Image []images;
}
[CustomEditor(typeof(Foo))]
class FooEditor : Editor {
public override void OnInspectorGUI()
{
DrawDefaultInspector();
Foo foo = (Foo)target;
if(GUILayout.Button("Bind last Button and all Images")) {
/* how to bind programatically last foo.GetComponentsInChildren<Button>();
to foo.gameObject.scene's
foo.gameObject.GetComponent<Foo>().lastButton
in edit mode's property window
(without have to drag on the editor)?
*/
/* and how to bind programatically all foo.GetComponentsInChildren<Image>();
to foo.gameObject.scene's
foo.gameObject.GetComponent<Foo>().images
in edit mode's property window
(without have to drag all one by one)?
*/
}
}
}
I want to automate the binding without have to drag one by one or do it in runtime, how can I do it programatically?
the only workaround I can think of (if there's no such API) is by saving the scene, parsing the scene yaml, then update the binding on the yaml file, and force UnityEditor to reload the yaml, but not sure if it's gonna work since loading yaml and rewriting it doesn't give equal value
Though I still don't understand the purpose of "binding" to be honest, it seems to me you actually are talking about simply referencing all the components via the editor script instead of having to drag and drop them. (Maybe the binding is included here and I just don't know it under that name?)
First of all make sure that either the entire code for FooEditor is placed in a folder called Editor or wrap it in
#if UNITY_EDITOR
// any code using UnityEditor namespace
#endif
in order to strip them off in a build later. Otherwise you'll get compiler errors since UnityEditor will not exist in a build.
Then the important thing is to allways manipulate the SerializedPropertys of the SerializedObjects in editor scripts. This handles all the marking as dirty, saving changes and Undo/Redo entries for you. If you should happen to manipulate rather the fieds directly you would have to take care of these things yourself ...
a keyrole herefore play SerializedObject.Update and SerializedObject.ApplyModifiedProperties for loading and writing back values between the real target component and its serialized object - I like to call it "shadow clone".
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
class Foo : MonoBehaviour
{
public Button lastButton;
public Image[] images;
}
#if UNITY_EDITOR
[CustomEditor(typeof(Foo))]
class FooEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// fetch current values from the real target into the serialized "clone"
serializedObject.Update();
Foo foo = (Foo)target;
if (GUILayout.Button("Bind last Button and all Images"))
{
// get last button field as serialized property
var lastButtonField = serializedObject.FindProperty("lastButton");
// get last Button reference
// pass true to also get eventually inactive children
var buttons = foo.GetComponentsInChildren<Button>(true);
var lastButton = buttons[buttons.Length - 1];
// asign lastButton to the lastButtonField
lastButtonField.objectReferenceValue = lastButton;
// slightly more complex but similar
// get the field as serialized property
var imagesField = serializedObject.FindProperty("images");
// get the images references
// again pass true to also get eventually inactive ones
var images = foo.GetComponentsInChildren<Image>(true);
// now first set the according list size
imagesField.arraySize = images.Length;
// assign all references
for (var i = 0; i < imagesField.arraySize; i++)
{
// serialized property of the element in the field list
var entry = imagesField.GetArrayElementAtIndex(i);
// simply assign the reference like before with the button
entry.objectReferenceValue = images[i];
}
}
// write back changes to the real target
// automatically handles marking as dirty and undo/redo
serializedObject.ApplyModifiedProperties();
}
}
#endif
I hope I'm not completely off and this is what you ment by "binding".
Note if you want to further change values of a reference you optained which is not the target itself you can easily use new SerializedObject(objectReference) in order to get a serialzed object e.g.
var serializedLastButton = new SerializedObject (lastButton);
Or also
var serializedLastButton = new SerializedObject(lastButtonField.objectReferenceValue);
now when changing any field in it again using FindProperty(propertyName) make sure to again also use
serializedLastButton.Update();
// make changes
serializedLastButton.ApplyModifiedProperties();

C# CheckedListBox how to manipulate checked data?

Hey guys new to C# and I am trying to setup a GUI, all I want the GUI to do is have a simple file explorer with a CheckedListBox to represent selected files.
I can get the CheckedListBox to show up and click on files but I'm not sure how to continue from here, most tutorials stop here, or go too advanced with tree view and other things that seem unnecessary for what I am trying to do.
Here is my code:
Any help is appreciated and if you guys could point me in the right direction that would be awesome.
EDIT:
To rephrase my question:
I want the user to select files through the CheckedListBox (user input stops here), and for those selected files to be put in a list that my code can manipulate.
Not sure how to accomplish this after my first foreach loop (which adds all files in the selected directory to the CheckedListBox for user selection).
The second foreach loop is an attempt at this, manipulating the files so that they output their filenames after being selected. However no Messagebox shows up and I assume that their is a disconnect between the user selecting files and the codes attempt at manipulating said files.
Second Edit:
I think I figured it out I made a second button and from here it looks like I can manipulate the chosen files however I want.
Currently the code is working the way I would expect it to work.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace SelectFiles
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
checkedListBox1.CheckOnClick = true;
}
private void button1_Click(object sender, EventArgs e)
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
MessageBox.Show(fbd.SelectedPath);
checkedListBox1.Items.Clear();
string[] files = Directory.GetFiles(fbd.SelectedPath);
foreach (string file in files)
{
checkedListBox1.Items.Add(file);
}
}
private void button2_Click_1(object sender, EventArgs e)
{
List<string> list_all_excelfiles = new List<string>();
foreach (string item in checkedListBox1.CheckedItems)
{
list_all_excelfiles.Add(item);
MessageBox.Show(Path.GetFileName(item));
}
}
}
}
First i advice you to assign Value member and Display member for each item.
Display Member will be visible to user
Value Member will we use in code
To do this first create simple custom class
public class Int_String
{
public int _int { get; set; }
public string _string { get; set; }
}
Be careful because get; set; part is important to be there since if not code will not work
Now what you need to do is create list of items with custom class like this
class YourForm : Form
{
List<Int_String> myList = new List<Int_String>(); //Create list of our custom class
public YourForm()
{
PopulateMyList();
}
private void PopulateMyList()
{
//Here read from database or get data somehow and populate our list like this
//I will populate it manually but you do it in foreach loop
myList.Add(new Int_String { _int = 0, _string = "First Item" });
myList.Add(new Int_String { _int = 1, _string = "Second Item" });
myList.Add(new Int_String { _int = 2, _string = "Third Item" });
}
}
After that you need to assign this list to your checkedListBox which you will do like this:
public YourForm()
{
PopulateMyList();
checkedListBox1.DataSource = myList;
checkedListBox1.DisplayMember = "_string";
checkedListBox1.ValueMember = "_int";
}
And now when you can manipulate with checked items like this:
for(int i = 0; i < checkedListBox1.Items.Count; i++)
{
if(checkedListBox1.Items[i].CheckedState == CheckState.Checked)
{
int itemValueMember = (checkedListBox1.Items[i] as Int_String)._int;
int itemDisplayMember = (checkedListBox1.Items[i] as Int_String)._string;
//Use these two vars for whatever you need
}
}
TWO IMPORTANT TIPS:
I am not sure for this one since i am writing all this from head but i think that visual studio will not show you that there is DisplayMember or ValueMember for checkedBox component BUT also it will not show error. Reason is that they have hidden in intentionally for idk what reason but it will work.
You are able to assign Display and Value member to a lot of components in winforms BUT for some reason checkedListBox is specific. It is specific because you MUST first assign DataSource to it and then tell it checkedListBox.DisplayMember = "_string" ...... For new guy you will ask why it is important. Simple answer is create custom list for test and add 10k items inside it and then first declare datasource and after it Display and Value member. Test how long form will need to load (get out of freeze state). After that do everything same but first declare Display and Value member and then assign datasource and test again. I am telling this from head without testing but before when i needed about 5k rows with 1st solution it took me about 30 sec and second < 1 sec. If you want to know more about it google it but for now this is pretty much info for you.

Trying to pass a list to a form by value not reference

Working on a project where I am storing my data using a GenericList, based off a class I created which contains a handful of string members. This works great, but things get troublesome when I try to pass this list to a second form. I initially setup a get/set block in the second form to accept the list and then return it, but the destination is always null. As a workaround I changed the local list on the second form to public and am able to access it that way, but then instead of getting a copy of the list, I get ByRef passing so any changes made are reflected in the master list. Since I want to be able to not accept these changes, I really want this to be a pass by value. These two problems have to be linked...
Here is the setup. In form1 I have this definition at the class level, making this somewhat of a global variable (yeah its bad, but it works for me):
private List<ServerList> theServerList = new List<ServerList>();
Later on I create the new form and (try to) pass my data into it using:
frmEditor theEditor = new frmEditor();
theEditor.updatedServerList = theServerList;
DialogResult res = theEditor.ShowDialog();
On the second from, I have this to receive the data:
private List<ServerList> myServerList = new List<ServerList>();
public List<ServerList> updatedServerList
{
get { return myServerList; }
set { myServerList = updatedServerList; }
}
This results a list on form2 (the myServerList) always being empty. Since this was not working, I commented out all these lines and changed the myServerList definition on form2 to public. Now after instantiating form2 I am able to get the data over by doing this:
theEditor.myServerList = theServerList;
This works in that the data nicely shows up form2, but this kind of an assignment just copies the pointer of the data block in memory from one variable to the other (basically a ByRef passing), so any changes I make on form2 change "both" lists since they are the same. I would like to have a local copy in form2 so I can make changes and then accept them or not depending of if the user clicks Cancel (drop all changes), or OK (copy the local data from form2 back to form1).
Your original does not work because your setter should be:
public List<ServerList> updatedServerList
{
get { return myServerList; }
set { myServerList = value; }
}
If you want to copy the list, you can use ToList on the incoming value:
public List<ServerList> updatedServerList
{
get { return myServerList; }
set { myServerList = value.ToList(); }
}
This is not to do with 'by value' vs 'by reference' since List<T> is a reference type, so the value of a variable of type List<ServerList> is a reference which is copied into the setter method. This reference will point to the same object in both the caller and receiver classes. If you want to copy the contents of the list you need to do it manually using ToList or similar.

How do you get the index of a column by the column name in an IList?

I apologize if this happens to be an easy question, but I'm new to coding. I've been banging my head against a wall with this problem for several days now on and off and have done numerous internet searches, but I keep coming up empty handed.
I have captured data from a database view into an IList. Because the data comes from a view and might (potentially) change the order of how columns appear should changes ever need to be made (columns added, etc), I want to programmatically pull the column's index from the IList via the column name.
This is easy enough to do with a gridview since it has a nice "HeaderRow" property that allows me to write a bit of code to loop through any row and pull the headerRow name, compare it to the name I'm looking for and then pull an incremented count number when I find it, and Voila!, I then have the index.
The problem is, I'm trying to avoid having to copy data from a list that was already copied from the entity framework that was copied from the database itself into another container (gridview) in order to have to manipulate it. It just seems like it's getting needlessly complex involving a gridview.
So, if anyone can tell me if there's a useful property of the IList class that I can use to acquire the column index # via the column name, please let me know and many thanks!
Edit:
In response to nvoigt's comment "An IList does not know anything about columns."
I'm sure it must have some idea, because when I step through the code and look at the first row data in the IList, it has the column names there ("AppAnswer", etc). All I want is some way to look through them and determine (by property or by code) what index that column name sits at. See photo for clarification.
I've had to link it here since my rep isn't high enough to post photos: https://www.dropbox.com/s/2xg3nduhqnzbl8n/ILisst.JPG
Also, this is the code that captures the data from the view and puts it into the list:
IList<View_AppQnA_All> list1 = useful.GetQuestionAnswerListForJobPosition(1);
public IList<View_AppQnA_All> GetQuestionAnswerListForJobPosition(int jobID)
{
IList<View_AppQnA_All> QuestionAnswerList = new List<View_AppQnA_All>();
IQueryable<View_AppQnA_All> view = Repository.Current.ObjectContext.
View_AppQnA_All.Where(Q => Q.AppQuestionForJobPosition == jobID);
QuestionAnswerList = view.ToList<View_AppQnA_All>();
return QuestionAnswerList;
}
#region Imports
using System;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataAccess;
#endregion
namespace AOJobApplication
{
class Repository
{
#region Declarations
private static Repository m_instance;
private static AOApplicationContext CWEntities;
// Lock synchronization object
//private static object syncLock = new object();
private static readonly object syncLock = new object();
#endregion
#region Properties
/// <summary>
/// Returns the one and only Repository
/// </summary>
public static Repository Current
{
get
{
if (m_instance == null)
{
lock (syncLock)
{
if (m_instance == null)
{
m_instance = new Repository();
}
}
}
return m_instance;
}
}
#endregion
/// <summary>
/// Object context to get entity objects application void
/// </summary>
public AOApplicationContext ObjectContext
{
get
{
if (CWEntities != null)
{
return CWEntities;
}
else
{
CWEntities = new AOApplicationContext();
return CWEntities;
}
}
}
}
}
The object you store in your list has properties, modeled after your view. Properties in a class don't have any order. They are just properties, they exist next to each other. You can order them in any way you like, the Visual Studio debugger seems to order them by name.
Those classes are generated, not dynamically built on the fly. If your view changes drastically (I guess by more than just field order), you will have to generate them again. This means recompile.

Categories

Resources