I've created a simple script that I can't debug. Here is my issue :
I'm looking to store the content of a directory into a variable in SSIS with Visual Studio 2015.
I've created a variable in my SSIS package, and set it's data type to Object.
I've added to my package a Script Task, that contains this code :
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
#endregion
namespace ST_c6399821104c42c2859b7b2481055848 {
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase {
public void Main() {
string CSVFilesCompletePath;
if (Dts.Variables.Contains("User::CSVFilesPathAbsolute") == true
&& Dts.Variables.Contains("User::CSVFilesPathRelativeCountry") == true
&& Dts.Variables.Contains("User::CSVFilesCountryObject") == true) {
CSVFilesCompletePath = Dts.Variables["User::CSVFilesPathAbsolute"].ToString() + Dts.Variables["User::CSVFilesPathRelativeCountry"].ToString();
String[] fileListTable = Directory.GetFiles(CSVFilesCompletePath, "*.xlsx");
List<string> fileList = new List<string>(fileListTable);
Dts.Variables["User::CSVFilesCountryObject"].Value = fileList;
}
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
As explained here :
SSIS Script Task Get File Names and Store to an SSIS Object Variable
But this code returns the following error when I try to Start it through the SSIS Job :
Variables are correctly set in the Script Wizard as ReadWriteVariables.
The thing is that my code shows this error when I try to affect the SSIS Variable and try to put the String[] in it.
Try adding a try-catch in the
Directory.GetFiles
call or in the whole body, then set the error message and/or the stack trace of the exception into a write SSIS variable for debugging purposes.
Edit: Looks like #H.Fadlallah point out the problem. You should use the Value property of the DtsVariable, not the ToString()
My error was :
CSVFilesCompletePath = Dts.Variables["User::CSVFilesPathAbsolute"].ToString() + Dts.Variables["User::CSVFilesPathRelativeCountry"].ToString();
I had to use : Dts.Variables[].Value.ToString() instead of Dts.Variables[].ToString()
As I was using the name of the Variable instead of the content of the Variable, the GetFiles returned a null object, and it couldn't be stored inside my variable, creating the different errors.
Related
I try to get the "Last modified" excel file in a folder and load it in SSIS. I found a C# code to get the name of most recent excel sheet in a folder path, and copy that in the Script Task. The code is :
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
namespace ST_2e01f076aa4f46d692cf4b47f5587da9.csproj
{
[System.AddIn.AddIn("ScriptMain", Version = "1.0", Publisher = "", Description = "")]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region VSTA generated code
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
public void Main()
{
// TODO: Add your code here
var directory = new DirectoryInfo(Dts.Variables["User::VarFolderPath"].Value.ToString());
FileInfo[] files = directory.GetFiles();
DateTime lastModified = DateTime.MinValue;
foreach (FileInfo file in files)
{
if (file.LastWriteTime > lastModified)
{
lastModified = file.LastWriteTime;
Dts.Variables["User::VarFileName"].Value = file.ToString();
}
}
MessageBox.Show(Dts.Variables["User::VarFileName"].Value.ToString());
Dts.TaskResult = (int)ScriptResults.Success;
}
}
}
However, when I run the script task to test it, I get the following error:
I used the project name displaying in error in my code, but still does not work. Could you please kindly help me how to fix it as I am new to both SSIS and C#. Thanks
Here is an answer using Linq.
First add these namespaces
using System.Collections.Generic; //This gets you list
using System.Linq; //This allows you linq functions
//Here is your code
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(#"D:\Temp");
List<System.IO.FileInfo> fi = di.EnumerateFiles().ToList();
Dts.Variables["VarFileName"].Value = fi.Where(i=>i.Extension.ToLower()==".xls")
.OrderByDescending(i => i.LastWriteTime)
.Select(i => i.FullName).FirstOrDefault();
I have the following code which works fine when I use it within a Windows Forms application, however the application I'm writing needs to run as a Windows service, and when I moved my code into the Windows Service template in Visual Studio 2015 Community Edition, I get the following error.
Cannot implicitly convert type "MyWindowsService.Main" to "System.ComponentModel.ISynchronizeVoke". An explicit conversion exists (are you missing a cast?)
Could anyone shed some light on why I am getting this error, and what I need to do to resolve it?
The code which throws the error is the line below, and it is located within the OnStart method of my main class (named Main.cs). The code is used to create an instance of the DataSubscriber class (AdvancedHMI library).
dataSubscribers[dataSubscriberIndex].SynchronizingObject = this;
It has to have something to do with the fact that the code is in a Windows service template, because using this works perfectly in my forms application running the same code.
UPDATE
Correction, I've attempted to cast this to the required type, and now get the following error on run.
Additional information: Unable to cast object of type 'MyWindowsService.Main' to type 'System.ComponentModel.ISynchronizeInvoke'.
Code:
dataSubscribers[dataSubscriberIndex].SynchronizingObject = (System.ComponentModel.ISynchronizeInvoke)this;
UPDATE
I've included the entire contents of the Main.cs file from my Windows Service application.
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using AdvancedHMIDrivers;
using AdvancedHMIControls;
using MfgControl.AdvancedHMI.Drivers;
using MfgControl.AdvancedHMI.Controls;
using System.Collections.ObjectModel;
namespace PLCHistoricDataHarvester {
public partial class Main : ServiceBase {
private EthernetIPforCLXCom commObject = new EthernetIPforCLXCom();
private globals globals = new globals();
private Dictionary<String, String> operationLines = new Dictionary<String, String>();
private Dictionary<String, String> tags = new Dictionary<String, String>();
private Collection<DataSubscriber> dataSubscribers = new Collection<DataSubscriber>();
private int harvesterQueueCount = 0;
private string harvesterInsertValues = String.Empty;
public Main() {
InitializeComponent();
}
protected override void OnStart(string[] args) {
// Initialize our harvester program
initializeHarvester();
Console.WriteLine("The program has started");
}
protected override void OnStop() {
// Call code when the service is stopped
Console.WriteLine("Program has stopped");
Console.ReadLine();
}
public void initializeHarvester() {
// First, we connect to the database using our global connection object
globals.dbConn.DatabaseName = "operations";
if (!globals.dbConn.IsConnect()) {
// TODO: Unable to connect to database. What do we do?
}
// Second, we connect to the database and pull data from the settings table
globals.initializeSettingsMain();
// Set IP address of PLC
commObject.IPAddress = globals.getSettingsMain("Processor_IP");
// Pull distinct count of our parent tags (Machines ex: Line 1, etc)
operationLines = globals.getOperationLines();
// If we have at least 1 operation line defined...we continue
if (operationLines.Keys.Count > 0) {
//Now we loop over the operation lines, and pull back the data points
int dataSubscriberIndex = 0;
foreach (KeyValuePair<String, String> lines in operationLines) {
int line_id = int.Parse(lines.Key);
string name = lines.Value;
tags = globals.getTags(line_id);
// If we have at least 1 tag for this operation line, we continue...
if (tags.Keys.Count > 0 && tags["tags"].ToString().IndexOf(",") != -1) {
// Create our dataSubscriber object
dataSubscribers.Add(new DataSubscriber());
dataSubscribers[dataSubscriberIndex].SynchronizingObject = (ISynchronizeInvoke)this;
dataSubscribers[dataSubscriberIndex].CommComponent = commObject;
dataSubscribers[dataSubscriberIndex].PollRate = 1000;
dataSubscribers[dataSubscriberIndex].PLCAddressValue = tags["tags"];
dataSubscribers[dataSubscriberIndex].DataChanged += new EventHandler<MfgControl.AdvancedHMI.Drivers.Common.PlcComEventArgs>(subscribeCallback);
// Increment our dataSubscriberIndex
dataSubscriberIndex++;
}
}
}
}
private void subscribeCallback(object sender, MfgControl.AdvancedHMI.Drivers.Common.PlcComEventArgs e) {
// code removed as it is irrelevant
}
}
}
The error message says this:
An explicit conversion exists (are you missing a cast?)
So add a cast like this:
dataSubscribers[dataSubscriberIndex].SynchronizingObject = (ISynchronizeInvoke)this;
^^^^^^^^^^^^^^^^^^^^
//Add this
If you've got a console app, the easiest way to convert it to a windows service is by using Topshelf, a nuget package which lets you run in either console mode or nt service mode.
Here's the quickstart guide.
We use it to write services all the time and it helps you avoid this kind of fragile shenanigans.
I have a .csv file that looks like this:
#Example Company
#(999) 999-9999
#http://yourwebsite.com
#Report Date Range: Dec 26, 2013 - Dec 26, 2013
#Exported: Dec 26, 2013
#Twitter : Profile Summary
#Screen Name,Name,Description,Location,Followers,Following,Listed
SctaSa,statisticalgraph,statistical Screen- The official account for your
organization,Saudi Arabia,6775,8,75
So, I need to take specific data from the .csv file to be readable to SSIS Transformation, start from column "Screen Name" and remove the garbage data which start with # , to be look like that
Screen Name,Name,Description,Location,Followers,Following,Listed,Exported,Report Date Range
SctaSa,statisticalgraph,statistical Screen- The official account for your organization,Saudi Arabia,6775,8,75,26-Dec-13,26-Dec-13
i tried to use this C# script but it does not wore file (I'm not an expert in C# so I don't know what the problem is) I tried to use the following script to delete any line start with # but the file dose not transfare to the out put path; could you give me any suggestions?!
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
#endregion
namespace ST_a7b941606e0b40aa920bfe13fc81dc81
{
/// <summary>
/// ScriptMain is the entry point class of the script. Do not change the name, attributes,
/// or parent of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
protected void Page_Load(object sender, EventArgs e)
{
var lines = new List<string>();
string line;
using (var file = new System.IO.StreamReader("D:\\try.csv"))
{
while ((line = file.ReadLine()) != null)
{
if (line.Length != 0)
{
if (!line.StartsWith("#") )
{
lines.Add(line);
}
}
}
}
File.WriteAllLines("D:\\SCTA_ETL\\try.csv", lines);
}
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
// TODO: Add your code here
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
Another way:
File.WriteAllLines(outputPath, File.ReadAllLines("c:\\mycsv.csv").Where(x => !x.StartsWith("#")).ToArray());
You might want to change your logic in the middle:
var lines = new List<string>();
string outputPath = // your output path here
using (var file = new System.IO.StreamReader("c:\\mycsv.csv"))
{
string line;
while ((line = file.ReadLine()) != null)
{
if (!line.StartsWith("#"))
{
lines.Add(line);
}
}
}
File.WriteAllLines(outputPath, lines);
You had been removing all lines that had "#" anywhere inside.
Instead, only add in lines that do not start with "#".
Also, be sure to close and dispose your StreamReader when you are done with it, or just put the whole thing in using section.
I'm using Visual Studio 2013 to create a Visual C# Windows Forms Application and I'm not using the Designer to setup the form.
I'm trying to use a Dictionary to store Bitmaps so that I can call them later by name. But when I debug the script I get the error:
An unhandled exception of type 'System.NullReferenceException' occurred in SimpleForm.exe
Additional information: Object reference not set to an instance of an object.
From the line:
width = imgLetters["a"].Width;
Any help would be greatly appreciated.
Cut down version of code which still produces the error:
using System;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace SimpleForm
{
public class Test : Form
{
static Bitmap bmpLetterA;
static Bitmap bmpLetterB;
static Bitmap bmpLetterC;
private Dictionary<string, Bitmap> imgLetters;
public Test()
{
ImgInitialize();
ImgWidth();
}
private void ImgInitialize()
{
Dictionary<string, Bitmap> imgLetters;
bmpLetterA = new Bitmap("a.png");
bmpLetterB = new Bitmap("b.png");
bmpLetterC = new Bitmap("c.png");
imgLetters = new Dictionary<string, Bitmap>();
imgLetters.Add("a", bmpLetterA);
imgLetters.Add("b", bmpLetterB);
imgLetters.Add("c", bmpLetterC);
}
private void ImgWidth()
{
int width = 0;
width = imgLetters["a"].Width;
}
}
}
Remove the line Dictionary<string, Bitmap> imgLetters; from ImgInitialize. This creates a local variable with the same name as the member variable. It is then filled, but never used, while the member variable remains uninitialized.
Tipps for avoiding problems like this:
You could name instance members in a special way to make clear the variable is an instance member (for example m_member instead of member).
You could prefix access to an instance member with this. to make clear which variable you want to access.
You could try to avoid naming local variables the same as instance members.
The problems lies here:
private void ImgInitialize()
{
Dictionary<string, Bitmap> imgLetters;
This variable shadows the class's field private Dictionary<string, Bitmap> imgLetters;. Remove this declaration from the method ImgInitialize and it'll work fine.
Friendly tip to prevent such mistakes in the future: when using a class member, append this:
this.imgLetters = new Dictionary<string, Bitmap>();
this.imgLetters.Add("a", bmpLetterA);
this.imgLetters.Add("b", bmpLetterB);
this.imgLetters.Add("c", bmpLetterC);
This way, even if you have local variable with the same name it won't interfere.
The problem is in your ImgInitialize() method.
remove this Line
Dictionary<string, Bitmap> imgLetters;
What you are doing is creating a local variable with the same name as your global variable. So it is this one that you are adding values to. This then lost when the method has completed due to its scope.
Your global variable is still null at this point and this is why the error occurs.
how do we process a cube or access OLAP database through ASP.Net with C# code? what is the component to be used, in C#.Net for connecting OLAP database or process actions in anaysis Services ?
For processing, use Microsoft.AnalysisServices library, example code looks like:
Server server = new Server();
server.Connect(cubeConnectionString);
Database database = server.Databases.FindByName(databaseName);
Cube cube = database.Cubes.FindByName(cubeName);
cube.Process(ProcessType.ProcessFull);
For querying, use Microsoft.AnalysisServices.AdomdClient library, example code looks like:
using (Adomd.AdomdConnection adomdConnection = new Microsoft.AnalysisServices.AdomdClient.AdomdConnection())
{
adomdConnection.ConnectionString = cubeConnectionString;
Adomd.AdomdCommand adomdCommand = new Microsoft.AnalysisServices.AdomdClient.AdomdCommand();
adomdCommand.Connection = adomdConnection;
adomdCommand.CommandText = mdxQuery;
adomdConnection.Open();
cellSet = adomdCommand.ExecuteCellSet();
adomdConnection.Close();
}
Note that the two namespaces overlap, so you may need to alias if you use them in the same place.
http://msdn.microsoft.com/en-US/library/ms124924(v=SQL.90).aspx
http://msdn.microsoft.com/en-us/library/ms123483(v=SQL.90).aspx
You must process the database, not the cube. Because the cube has only the measures not the dimensions inside. This can give some conflicts.
To Prozess all, Cubes and Dimensions you must process the whole database:
Server server = new Server();
server.Connect(cubeConnectionString);
Database database = server.Databases.FindByName(databaseName);
database.Process(ProcessType.ProcessFull);
Answer for this already shared above but just sharing that I too had used the same Microsoft.AnalysisServices by API referring the sample project downloaded from my blog post to process cube from C# but when the dimension data gets changed then you need to process the database rather cube.
Also you can use EffectiveUserName property of connection string when an end user identity must be impersonated on the server.
NOTE: To use EffectiveUserName property, the caller must have administrative permissions in Analysis Services.
This example was done with Visual Studio Express 2012 and $44 copy of Ms SQL 2012 (God bless Microsoft for providing so much functionality for so little money). The OS was Win 8 pro.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//the next 2 using's had to be downloaded and "Add Reference"d for Visual Studio Express 2012
using Microsoft.AnalysisServices;
using Microsoft.AnalysisServices.AdomdClient;
using System.Windows.Forms;
using System;
using System.Data;
using System.Drawing;
namespace SSASDataview
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void RunSSAS(object sender, EventArgs e)
{
//i don't think Dataset is in the Analysis Services directives
DataSet ds = new DataSet();
// provider is the constant olap. datasource is the same server name you provide for Mgmt Studio or localhost
// initial catalog is tricky and important. It is not a standard ms sql database you see in Management Studio,
// even if your cube was create with tables from a particular database.
// the only place I was able to see "initial catalog" value was a File -> Open -> Analysis Services Database in 2012 Management Studio
// it was also the name of the VS2010 solution I used to create the cube.
AdomdConnection myconnect = new AdomdConnection(#"provider=olap;initial catalog=GLCubeThree;datasource=localhost");
AdomdDataAdapter mycommand = new AdomdDataAdapter();
mycommand.SelectCommand = new AdomdCommand();
mycommand.SelectCommand.Connection = myconnect;
// this query was created by the "Browser" you see for an Analysis Services project
// if you poke around the icons on the browser table the Design Mode icon will give you the cube query
// I think it's an MDX query, threre are also xml queries you can run with adomd
mycommand.SelectCommand.CommandText = "SELECT NON EMPTY { [Measures].[Per Balance] } ON COLUMNS, NON EMPTY { ([Gltime].[Fisc Per].[Fisc Per].ALLMEMBERS ) } DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS FROM ( SELECT ( { [Gltime].[Fisc Per].&[201301], [Gltime].[Fisc Per].&[201302], [Gltime].[Fisc Per].&[201307] } ) ON COLUMNS FROM [GL Cube]) CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS";
myconnect.Open();
mycommand.Fill(ds, "tbl");
myconnect.Close();
// the below assigns the results of the cube query to a dataGridView
// if you drag a dataGridView control to your pallete it will create exactly
// what you need for the line below to work.
// your project type has to be a Window Forms Applications
// this code shown here is in the default Form1.Designer.cs not Form1.cs
dataGridView1.DataSource = new DataView(ds.Tables[0]);
}
private void Quit_Click(object sender, EventArgs e)
{
this.Close();
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
#endregion
private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.Button runssas;
private System.Windows.Forms.Button quit;
}
}