How to map result sets from an execute SQL task using ManagedDTS - c#

I'm trying to generate some SSIS (2008) packages using C# (.Net 4.0) and ManagedDTS (10.0). So far I've managed to generate a package and add some connections and variables, now I'm trying to populate the control flow, the first task I'm trying to add is an "Execute SQL Task" to check if the source table has any rows so I can abort if it doesn't. How I'd do this if I was building the SSIS package manually is to have the Execute SQL task run a query like this:
SELECT RecordExists = CASE WHEN EXISTS (SELECT * FROM [Schema].[TABLE] WHERE [COLUMN] IS NULL) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END
I'd then set the ResultSet property to "Single row" and then map the Result to a variable:
However I can't see how to do this in C#, this is what I've got so far:
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Tasks.ExecuteSQLTask;
...
public Package SsisPackage;
...
SsisPackage.Variables.Add("VAR_RecordExists", false, "User", false);
Executable RecordExists = SsisPackage.Executables.Add("STOCK:SQLTask");
TaskHost thRecordExists = RecordExists as TaskHost;
thRecordExists.Properties["Name"].SetValue(thRecordExists, "Do Records Exist?");
thRecordExists.Properties["SqlStatementSource"].SetValue(thRecordExists, "SELECT RecordExists = CASE WHEN EXISTS (SELECT * FROM [Schema].[TABLE] WHERE [COLUMN] IS NULL) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END");
thRecordExists.Properties["Connection"].SetValue(thRecordExists, "Stage");
thRecordExists.Properties["ResultSetType"].SetValue(thRecordExists, ResultSetType.ResultSetType_SingleRow);
My first problem is that the last line errors with the error:
The name 'ResultSetType' does not exist in the current context
My second problem is that even if I get this working I have no idea how to actually map the result set to the variable, I've been using the documentation here but it doesn't mention anything about mapping result sets: https://learn.microsoft.com/en-us/sql/integration-services/building-packages-programmatically/adding-tasks-programmatically. I then found this: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dts.tasks.executesqltask.executesqltask.resultsettype.aspx?f=255&MSPPError=-2147217396 which tells me how to set the Result Set Type but results in the above error. It still doesn't mention how to set the result set mapping.
Any help is appreciated.

So I didn't find a way of doing this using the ManagedDTS Framework but I did get it to work.
What I ended up doing was:
Save the package without the result set mapping.
Open the package using XmlDocument
I created a helper class to this end and whilst it may not be the most efficient or clean code (I'm not a normally a C# developer) it does seem to do the job:
public class ResultSetBinding
{
string ResultName;
string DtsVariableName;
string TaskName;
public ResultSetBinding(string taskName, string resultName, string dtsVariableName)
{
TaskName = taskName;
ResultName = resultName;
DtsVariableName = dtsVariableName;
}
public void AddResultBinding(string filePath)
{
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlElement root = doc.DocumentElement;
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("DTS", "www.microsoft.com/SqlServer/Dts");
nsmgr.AddNamespace("SQLTask", "www.microsoft.com/sqlserver/dts/tasks/sqltask");
XmlNodeList executableNodes = root.SelectNodes("//DTS:Executable", nsmgr);
foreach (XmlNode executableNode in executableNodes)
{
if (IsExecuteSQLTask(executableNode, nsmgr))
{
if (IsSpecifiedTask(executableNode, nsmgr))
{
AddResultBindingToNode(doc, executableNode, nsmgr);
}
}
}
doc.PreserveWhitespace = true;
doc.Save(filePath);
}
private bool IsExecuteSQLTask(XmlNode executableNode, XmlNamespaceManager nsmgr)
{
foreach (XmlAttribute executableAttribute in executableNode.Attributes)
{
if (executableAttribute.Name == "DTS:ExecutableType")
{
return executableAttribute.Value.Contains("ExecuteSQLTask");
}
}
return false;
}
private bool IsSpecifiedTask(XmlNode executableNode, XmlNamespaceManager nsmgr)
{
foreach (XmlNode propertyNode in executableNode.ChildNodes)
{
if (propertyNode.Name == "DTS:Property")
{
foreach (XmlAttribute propertyAttribute in propertyNode.Attributes)
{
if (propertyAttribute.Name == "DTS:Name" && propertyAttribute.Value == "ObjectName" && propertyNode.InnerText == TaskName)
{
return true;
}
}
}
}
return false;
}
private void AddResultBindingToNode(XmlDocument doc, XmlNode executableNode, XmlNamespaceManager nsmgr)
{
foreach (XmlNode objectNode in executableNode.ChildNodes)
{
if (objectNode.Name == "DTS:ObjectData")
{
foreach (XmlNode sqlNode in objectNode.ChildNodes)
{
if (sqlNode.Name == "SQLTask:SqlTaskData")
{
XmlElement bindingElement = doc.CreateElement("SQLTask:ResultBinding", "www.microsoft.com/sqlserver/dts/tasks/sqltask");
bindingElement.SetAttribute("ResultName", "www.microsoft.com/sqlserver/dts/tasks/sqltask", ResultName);
bindingElement.SetAttribute("DtsVariableName", "www.microsoft.com/sqlserver/dts/tasks/sqltask", String.Format("User::{0}", DtsVariableName));
sqlNode.AppendChild(bindingElement);
}
}
}
}
}
}
Which I then set the following class member:
public List<ResultSetBinding> ResultBindings;
Add the result binding when creating the task:
ResultSetBinding ResultBinding = new ResultSetBinding("Do Records Exist?", "RecordExists", "VAR_RecordExists");
ResultBindings.Add(ResultBinding);
And then once saved loop through and add the result bindings:
foreach (ResultSetBinding ResultBinding in ResultBindings)
{
ResultBinding.AddResultBinding(FilePath);
}

Related

How to add two actions depending on 'type' attribute of element in XML using webdriver in c#

I have elements stored in a config.xml file as part of my project, currently I have a method to 'setData' which will find the element by the id and then set its value to the user input (using a webdriver instance called FireFoxBrowser)
I want to add a type attribute to the xml to differentiate between 'inputs' which will use the current code and 'button' to add code that will click anything with this type. How can I use webdriver to write this code?
public void setData(string elementName, string elementValue)
{
XmlDocument docXml = null;
try
{
docXml = new XmlDocument();
string xmlPath = new DirectoryInfo(Environment.CurrentDirectory).Parent.Parent.FullName + #"\config.xml";
docXml.Load(xmlPath);
XmlNode nd = docXml.SelectSingleNode(string.Format(#"//page[#url='{0}']", FireFoxBrowser.Url.ToString()));
if (nd != null)
{
var id = nd.SelectSingleNode(string.Format(#"element[#name='{0}']", elementName)).Attributes["id"].Value;
FireFoxBrowser.FindElement(By.Id(id)).Clear();
FireFoxBrowser.FindElement(By.Id(id)).SendKeys(elementValue);
}
}
finally
{
if (docXml != null)
docXml = null;
}
I was able to achieve this using the following line of code which differentiates between type attribute set:
var id = nd.SelectSingleNode(string.Format(#"element[#name='{0}']", elementName)).Attributes["id"].Value;

how to edit the nlog config file programmatically

i am trying to edit the logging level in the config file programmaticly.
foreach (var rule in LogManager.Configuration.LoggingRules)
{
if (m_loginglevelcomboBox.SelectedItem.ToString() == "Debug")
{
rule.EnableLoggingForLevel(LogLevel.Debug);
}
else
{
rule.EnableLoggingForLevel(LogLevel.Info);
}
}
//LogManager.ReconfigExistingLoggers();
I am not interested in calling the Reconfig,as the changes will affect the application on the fly.
I want the changes to be made when the application is restarted. so I need it to edit the config file.
i cant use xDocument ,as linq is not compatible with my .net version
so how can i edit the minlevel rule to debug/info ?
i used this to edit the logging level. I hope if this would help if some one stumbles across. If some one thinks it to be a bad idea, please let me know .
string configFilename = GetConfigFilePath();
XmlDocument doc = new XmlDocument();
doc.Load(configFilename);
XmlNode documentElement = doc.DocumentElement;
foreach (XmlNode node in documentElement.ChildNodes)
{
if (ruleDocumentNodeName.Equals(node.Name))
{
foreach (XmlNode childNode in node.ChildNodes)
{
if (loggerDocumentNodeName.Equals(childNode.Name))
{
XmlAttribute idAttribute = childNode.Attributes[minLevelAttributeName];
string currentValue = minLogingLevelComboBox.SelectedItem.ToString();
idAttribute.Value = currentValue;
doc.Save(configFilename);
MinLoggingLevelChanged = true;
}
}
}
}

Editing XML node inner text only if has specified value

Hello i need your super help.
Im not soo skilled in C# and i stack for about 6 hours on this. So please if anyone know help me . Thx
I have Xml like this
<COREBASE>
<AGENT>
<AGENT_INDEX>1</AGENT_INDEX>
<AGENT_PORTER_INDEX>
</AGENT_PORTER_INDEX>
<AGENT_NAME>John</AGENT_NAME>
<AGENT_SURNAME>Smith</AGENT_SURNAME>
<AGENT_MOBILE_NUMBER>777777777</AGENT_MOBILE_NUMBER>
</AGENT>
<AGENT>
<AGENT_INDEX>2</AGENT_INDEX>
<AGENT_PORTER_INDEX>1
</AGENT_PORTER_INDEX>
<AGENT_NAME>Charles</AGENT_NAME>
<AGENT_SURNAME>Bukowski</AGENT_SURNAME>
<AGENT_MOBILE_NUMBER>99999999</AGENT_MOBILE_NUMBER>
</AGENT>
</COREBASE>
And I need to select agent by index in windows forms combo box and than edit and save his attributes to xml. I found how to edit and save it but i dont know why but its saved to the first agent and overwrite his attributes in XML but not in the selected one.. :-(
Plese i will be glad for any help
private void buttonEditAgent_Click(object sender, EventArgs e)
{
XmlDocument AgentBaseEdit = new XmlDocument();
AgentBaseEdit.Load("AgentBase.xml");
XDocument AgentBase = XDocument.Load("AgentBase.xml");
var all = from a in AgentBase.Descendants("AGENT")
select new
{
agentI = a.Element("AGENT_INDEX").Value,
porterI = a.Element("AGENT_PORTER_INDEX").Value,
agentN = a.Element("AGENT_NAME").Value,
agentS = a.Element("AGENT_SURNAME").Value,
agentM = a.Element("AGENT_MOBILE_NUMBER").Value,
};
foreach (var a in all)
{
if ("" == textBoxEditAgentIndex.Text.ToString())
{
MessageBox.Show("You must fill Agent Index field !!", "WARNING");
}
else
{
// AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/AGENT_INDEX").InnerText == textBoxEditAgentIndex.Text
if (a.agentI == textBoxEditAgentIndex.Text.ToString())
{
AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/AGENT_INDEX").InnerText = textBoxEditAgentIndex.Text;
AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/AGENT_PORTER_INDEX").InnerText = textBoxEditAgentPorterIndex.Text;
AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/AGENT_NAME").InnerText = textBoxEditAgentName.Text;
AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/AGENT_SURNAME").InnerText = textBoxEditAgentSurname.Text;
AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/AGENT_MOBILE_NUMBER").InnerText = textBoxEditAgentMobile.Text;
AgentBaseEdit.Save("AgentBase.xml");
ClearEditAgentTxtBoxes();
}
}
}
}
Am i on the right way but i dont see the doors or i am totaly wrong ? Thx all. Miko
OK i tried it this way but it didnt changed the inner text
string agentIndex = comboBoxEditAgentI.SelectedItem.ToString();
XmlDocument AgentBaseEdit = new XmlDocument();
AgentBaseEdit.Load("AgentBase.xml");
XDocument AgentBase = XDocument.Load("AgentBase.xml");
var xElemAgent = AgentBase.Descendants("AGENT")
.First(a => a.Element("AGENT_INDEX").Value == agentIndex);
xElemAgent.Element("AGENT_MOBILE_NUMBER").Value = textBoxEditAgentMobile.Text;
xElemAgent.Element("AGENT_SURNAME").Value = textBoxEditAgentSurname.Text;
AgentBaseEdit.Save("AgentBase.xml");
It would be easier if you use Linq2Xml.
int agentIndex = 2;
XDocument xDoc = XDocument.Load(filename);
var xElemAgent = xDoc.Descendants("AGENT")
.First(a => a.Element("AGENT_INDEX").Value == agentIndex.ToString());
//or
//var xElemAgent = xDoc.XPathSelectElement(String.Format("//AGENT[AGENT_INDEX='{0}']",agentIndex));
xElemAgent.Element("AGENT_MOBILE_NUMBER").Value = "5555555";
xDoc.Save(fileName)
PS: namespaces: System.Xml.XPath System.Xml.Linq
It does not work, because you are selecting the first agent explicitly with in each loop
AgentBaseEdit.SelectSingleNode("COREBASE/AGENT/...")
But you can do it easier by reading and changing withing the same xml document. I'm only changing the agent name and replacing it with "test 1", "test 2", ...
XDocument AgentBase = XDocument.Load("AgentBase.xml");
int i = 0;
foreach (XElement el in AgentBase.Descendants("AGENT")) {
el.Element("AGENT_NAME").Value = "test " + ++i;
// ...
}
AgentBase.Save("AgentBase.xml");
UPDATE
However, I'm suggesting you to separate the logic involving the XML handling from the form. Start by creating an Agent class
public class Agent
{
public string Index { get; set; }
public string PorterIndex { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Mobile { get; set; }
}
Then create an interface defining the needed functionality for an agent repository. The advantage of this interface is that it will make it easier later to switch to another kind of repository like a relational database.
public interface IAgentRepository
{
IList<Agent> LoadAgents();
void Save(IEnumerable<Agent> agents);
}
Then create a class that handles the agents. Here is a suggestion:
public class AgentXmlRepository : IAgentRepository
{
private string _xmlAgentsFile;
public AgentXmlRepository(string xmlAgentsFile)
{
_xmlAgentsFile = xmlAgentsFile;
}
public IList<Agent> LoadAgents()
{
XDocument AgentBase = XDocument.Load(_xmlAgentsFile);
var agents = new List<Agent>();
foreach (XElement el in AgentBase.Descendants("AGENT")) {
var agent = new Agent {
Index = el.Element("AGENT_INDEX").Value,
PorterIndex = el.Element("AGENT_PORTER_INDEX").Value,
Name = el.Element("AGENT_NAME").Value,
Surname = el.Element("AGENT_SURNAME").Value,
Mobile = el.Element("AGENT_MOBILE_NUMBER").Value
};
agents.Add(agent);
}
return agents;
}
public void Save(IEnumerable<Agent> agents)
{
var xDocument = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("COREBASE",
agents.Select(a =>
new XElement("AGENT",
new XElement("AGENT_INDEX", a.Index),
new XElement("AGENT_PORTER_INDEX", a.PorterIndex),
new XElement("AGENT_NAME", a.Name),
new XElement("AGENT_SURNAME", a.Surname),
new XElement("AGENT_MOBILE_NUMBER", a.Mobile)
)
)
)
);
xDocument.Save(_xmlAgentsFile);
}
}
The form can now concentrate on the editing logic. The form does not even need to know what kind of repository to use if you inject the repository in the form constructor (of cause the form constructor must declare a parameter of type IAgentRepository):
var myAgentForm = new AgentForm(new AgentXmlRepository("AgentBase.xml"));
myAgentForm.Show();
UPDATE #2
Note that you cannot change a single item within an XML file. You must load all the agents, make an edit and then rewrite the whole file, even if you edited only one agent.
To do this, you can use my LoadAgents method, then pick an agent from the returned list, edit the agent and finally write the agents list back to the file with my Save method. You can find an agent in the list with LINQ:
Agent a = agents
.Where(a => a.Index == x)
.FirstOrDefault();
This returns null if an agent with the required index does not exist. Since Agent is a reference type, you don’t have to write it back to the list. The list is keeping a reference to the same agent as the variable a.

Programmatically access SharePoint lists and subsites?

How can I iterate SharePoint lists and subsites from a C# program? Is the SharePoint.dll from a SharePoint installation required for this, or is there a "Sharepoint client" dll available for remotely accessing that data?
Use the Sharepoint web services; in particular the Webs and Lists webservices do what you ask.
For Sharepoint 2007:
http://msdn.microsoft.com/en-us/library/bb862916(v=office.12).aspx
For Sharepoint 2007 you will need to access the web services. In Sharepoint 2010, there is a sharepoint client object model.
http://msdn.microsoft.com/en-us/library/ee857094%28office.14%29.aspx
I happen to be dealing with this very thing now... this works. I've dumbed down the code a bit to focus on just the mechanics. It's rough around the edges, but hopefully you get the idea. It's working for me.
Also, be sure to set up a web reference using the URL of your Sharepoint site. Use that as your "web reference" below.
private <web reference> _Service;
private String _ListGuid, _ViewGuid;
private Initialize()
{
_Service = new <web reference>.Lists();
_Service.Credentials = System.Net.CredentialCache.DefaultCredentials;
_Service.Url = "https://sharepointsite/_vti_bin/lists.asmx";
}
private String SpFieldName(String FieldName, Boolean Prefix)
{
return String.Format("{0}{1}", Prefix ? "ows_" : null,
FieldName.Replace(" ", "_x0020_"));
}
private String GetFieldValue(XmlAttributeCollection AttributesList,
String AttributeName)
{
AttributeName = SpFieldName(AttributeName, true);
return AttributesList[AttributeName] == null ?
null : return AttributesList[AttributeName].Value;
}
public void GetList()
{
string rowLimit = "2000"; // or whatever
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
System.Xml.XmlElement query = xmlDoc.CreateElement("Query");
System.Xml.XmlElement viewFields = xmlDoc.CreateElement("ViewFields");
System.Xml.XmlElement queryOptions =
xmlDoc.CreateElement("QueryOptions");
queryOptions.InnerXml = "";
System.Xml.XmlNode nodes = _Service.GetListItems(_ListGuid, _ViewGuid,
query, viewFields, rowLimit, null, null);
foreach (System.Xml.XmlNode node in nodes)
{
if (node.Name.Equals("rs:data"))
{
for (int i = 0; i < node.ChildNodes.Count; i++)
{
if (node.ChildNodes[i].Name.Equals("z:row"))
{
XmlAttributeCollection att =
node.ChildNodes[i].Attributes;
String title = GetFieldValue("Title");
String partNumber = GetFieldValue("Part Number");
}
}
}
}
}
}
Also, the SpFieldName method is not iron-clad. It's just a good guess, for most field names in a list. This, unfortunately, is a journey of discovery. You need to expose the XML to find the actual field names if they don't match.
Good hunting.

Test for root node XML .NET

I'm currently using the code below to attempt to check for a certain root node (rss) and a certain namespace\prefix (itunes), but it seems to be saying that the feed is valid even when supplied with a random web page URL instead of one pointing to a feed.
FeedState state = FeedState.Invalid;
XmlDocument xDoc = new XmlDocument();
xDoc.Load(_url);
XmlNode root = xDoc.FirstChild;
if (root.Name.ToLower() == "rss" && root.GetNamespaceOfPrefix("itunes") == "http://www.itunes.com/dtds/podcast-1.0.dtd")
{
state = FeedState.Valid;
}
return state;
Can anybody tell me why this might be?
Found the solution now. Putting xDoc.Load(_url); in a try .. catch block and returning FeedState.Invalid upon exception seems to have solved my problems.
FeedState state = FeedState.Invalid;
XmlDocument xDoc = new XmlDocument();
try
{
xDoc.Load(_url);
}
catch
{
return state;
}
XmlNode root = xDoc.FirstChild;
if (root.Name.ToLower() == "rss" && root.GetNamespaceOfPrefix("itunes") == "http://www.itunes.com/dtds/podcast-1.0.dtd")
{
state = FeedState.Valid;
}
return state;

Categories

Resources