Is there any way to make an - XML element value - dynamic? - c#

I know that this question seems illogical, but I have to try, and I hope to find a way,
I've a DLL assembly reading a variable, "GalleryID", from an XML file. I can't modify the assembly, so I am wondering if I can make the XML element value dynamic? For example, to get this value from a querystring or any other scenario without changing the assembly.
In Settings.xml:
< ?xml version="1.0" encoding="utf-8"? >
< GallerySettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
< GalleryID >2< /GalleryID > // Here I would like this value to be dynamic, not static like this.
< /GallerySettings >
In mylibrary assembly:
protected static XmlDocument myXmlDoc;
' in Page_Load ev mylibrary
MyLibrary.myXmlDoc.Load(base.Server.MapPath("Conntrols/Settings.xml"));
XmlNode documentElement = MyLibrary.myXmlDoc.DocumentElement;
MyLibrary.myappSettings.GalleryID = documentElement.SelectSingleNode("GalleryID").ChildNodes[0].Value.ToString();
This is an ASP.NET website that has multiple users. When a user logs in and open the gallery.aspx page it will show the user's own gallery. This scenario is not working now since the gallery module shows one gallery (Galleryid=2) to all users according to its setting in Settings.xml file. How can I pass galleryid in querystring -- gallery.aspx?galleryid=5 and inject it to Settings.xml file?
Is that possible?

I am going to restate what I understand from your question to make sure I 'get it'.
You have an assembly (from a .dll) that reads an XML file as a data source to get the "GalleryID" out of a node that looks like: <GalleryID>2</GalleryID>
You want to be able to change the GalleryID before the assembly reads it.
I assume (yes, dangerous...) that you are invoking your assembly from an application that you DO have control over. (Since, presumably you don't have control over the assembly that reads the XML doc.) This would be the calling context, and the host that defines exactly where the "Server" in Server.MapPath is going to look for "Conntrols/Settings.xml"
There are two basic solutions that come to mind:
Open the XML document, change the value and save it from your calling context.
Swap out XML document "settings.xml.alt" for "settings.xml" from your calling context.

Would this not be better off being done in an in-process database?
You are explaining to me that you are going to have a lot of different galleries. In this way, with that much dynamic data, I think you would be better to use an in-process database such as SQL Server Compact and could be easily done using the Entity Framework code. First, get the package off of NuGet. If you have control over your data source I would personally go this route.

Related

SSIS:Capture [FileCreationDate] and [FileSize] of .csv files in separate user variables,inside a ForEachLoop Container using Script Task (C#) in CFlow

I have good experience in SSIS, but nil as far as Script Task and Script Component are concerned.
I have a situation in which I am loading many .csv files into a SQL Server destination table. I am using a For Each Loop Container.
I am loading these files, capturing the File Name, Row Count, and other information needed into various SSIS user variables.
I load some of these variables into an Audit Tracker table using an Execute SQL Task in the Control Flow, after the Data Flow Task (all inside a For Each Loop Container)
Upto this point, everything is fine.
Now our client wants to capture the FileCreationDate (i.e.Date Modified) and FileSize of each of these .csv files. I need to capture them in separate user variables.
Obviously I need help, because this cannot be done without some scripting.
In the Control Flow, inside the ForEachLoop Container, I have added a ScriptTask immediately after the Data Flow Task.
I have the following three user variables in the package:
1) User::Filename (string variable for each.csv files;this is fine, no issue)
2) User::FileCreatedDateTime (variable to capture the creation date of each .csv file; please let me know whether DateTime datatype will be okay)
3) User::FileSize (variable to capture the size of each .csv file; let me know whether string datatype will be okay)
Now, can anyone let me know the following settings inside the Script Task Editor ?
Script Tab :
Language as Microsoft Visual C# 2017 (disabled)
Entry Point: Main (Is this correct?)
ReadOnlyVariables: (What should I set ?)
ReadWriteVariables: (What should I set ?)
Expressions Tab:
What expressions should I set ? i.e. Property and Expression.
Edit Script:
What code should I type ? I referred to these two websites, but not able to get the right code
http://www.techbrothersit.com/2011/02/ssis-load-files-information-to-sql.html
SSIS Read file modification date
Can someone please give me the clear code and clarify the other doubts ?
Add Using System.IO; name space
For File CreationDate
DateTime creation_time = File.GetCreationTime(#"C:\test.txt");
Get file size
long filesize = File.Length;
write both variables to an output variable.
All of these components are readily explained in google. In fact I am going through google right now to refresh my memory and write this half baked answer.
Please go through this, debug my code, and post the correct code when you get it working. This is unlikely to work first try and you'll need to debug but you'll learn more if you actually do debug it.
Script Tab :
Language as Microsoft Visual C# 2017 (disabled)
I suggest you use C#, not VB as there are more examples online for C#. So leave as is
Entry Point: Main (Is this correct?)
This simply designates the name of the function that will be automatically called within the script task... which in the template is Main so leave as is
ReadOnlyVariables: (What should I set ?)
These are the variables which your script task needs to read (but not write back to). So this should have User::Filename in it. Use the UI to fill it in.
ReadWriteVariables: (What should I set ?)
These are the variables which your script task needs to read and write back to. So this should have something like User::FileCreatedDateTime,User::FileSize in it. Use the UI to fill it in.
Now when you hit the Edit Script.. button you'll get a template script.
Note
This already has a Main method in it
It has extensive instructions in the comments on how to read and write variables
(this is adapted from here and probably doesn't work without debugging http://www.techbrothersit.com/2011/02/ssis-load-files-information-to-sql.html)
Open out the namespaces region and add System.IO:
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
#endregion
Go the main method at the bottom and edit it:
public void main()
{
// Get the file path from the SSIS variable
string FilePath = Dts.Variables["User::Filename"].Value.ToString();
// Use the FileInfo method from System.IO to get the file object
FileInfo file = new FileInfo(FilePath);
// Save file properties back to SSIS variables
Dts.Variables["User::FileCreatedDateTime"].Value = file.CreationTime;
Dts.Variables["User::FileSize"].Value = file.Length;
// Return success
Dts.TaskResult = (int)ScriptResults.Success;
}
I note that this code is very similar to the second link you posted - if you are having problems with example code you need to explain why (i.e. specific error message)
You could also have a crack at this custom component https://archive.codeplex.com/?p=filepropertiestask but I strongly recommend never using custom components in your SSIS packages as they are usually a hassle down the line.

Read/Write SharePoint User Field [Word 2010 VSTO]

I've been experimenting with reading SharePoint 2013 Site Column metadata from within a Word 2010 Application-level C# VSTO.
For testing I've set-up Site Columns for every type that SharePoint uses, then created a Document Content Type that ties to them all -- thus all these columns are embedded into the Word document (looks to be stored within customXml within the document file).
By reading from the _Document.ContentTypeProperties property within the VSTO's code, I can access most types, but I'm having difficulty accessing a 'Person or Group' Site Column's data -- I'm getting COM Exceptions attempting to read or write to an item's .Value property.
By looking at the XSD schema in customXml, I can see a single-value User column is made up of three values: DisplayName (type string), AccountType (type string) and AccountId (type UserId) -- however I don't see a way to read/write from/to this within the VSTO? Multi-value User columns appear to be completely different, and are made up of two string values: an ID (appears to be the SharePoint user's ID) and a string-based ID (or at least that's what I think the i:0#.w|domain\userid is, anyway).
Word itself can edit both single- and multi-valued User column data via the Document Panel, but only if Word is currently connected to SharePoint -- otherwise the functionality is disabled. I'd assume the same would be true for the VSTO, if I could access the values at all...
My two questions are:
Is there a way to read/write single- and multi-value User fields from within VSTO code (even if it's not via the _Document.ContentTypeProperties property)?
Is there a way to do Q1 when if not connected to SharePoint (if, say, the values are known to the code)?
(I've been somewhat overly verbose in case my workings so far are useful to someone else even if I get no answers; there doesn't seem to be a great amount of information about this anywhere)
With some provisos, I believe you can do read/update these fields using VSTO - although I haven't actually created a working example using VSTO, the same objects as I'd use in Word VBA are available - the code snippets below are VBA.
The person/group values that are displayed in the DIP are stored in a Custom XML Part, even when the SharePoint server is unavailable. So the problem is not modifying the values - it's a CRUD operation, in essence - but knowing what values you can use, particularly in the multi-valued case. If you know how to construct valid values (let's say you have an independent list of email addresses) then you can make the modifications locally. Personally, I don't know how I would construct a valid value for the multi-valued case so I'd basically have to contact the server.
So assuming you have the data you need to update locally...
When SharePoint serves a Word Document, it inserts/updates several Custom XML Parts. One contains a set of schemas (as you have discovered). Another contains the data. All you really need to do is access the correct Custom XML Part, find the XML Element corresponding to your SharePoint user/group column, then it's a CRUD operation on the subElements of that Element.
You can find the correct Custom XML Part using the appropriate namespace name, e.g.
Const metaPropDataUri as String = _
"http://schemas.microsoft.com/office/2006/metadata/properties"
Dim theDoc as Word.Document
Dim cxp as Office.CustomXMLPart
Dim cxps as Office.CustomXMLParts
Set theDoc = ActiveDocument
Set cxps = theDoc.CustomXMLParts.SelectByNamespace(metaPropDataUri)
If there is more than one part associate with that Namespace, I don't know for sure how to choose the correct one. AFAIK Word/Sharepoint only ever creates one, and experiments suggest that if there is another one, SharePoint works with the first one. So I use
Set cxp = cxps(1)
At this point you need to know the XML Element name of the person/group column. It may be the same as the external name (the one you can see in the SharePoint list), but if for example someone called the Sharepoint column "person group", the Element name will be "person_x0020_group". If the name isn't going to vary, you can get it from the schema XML as a one-off task. Or it may be easy to generate the correct element name from any given SharePoint name. Otherwise, you can get it dynamically from the Schema XML, which you can get (as a string) using
theDoc.ContentTypeProperties.SchemaXML
What you need to do then is find the element with attribute ma:displayName="the external name" and get the value of the name attribute. I would imagine that's quite straightforward using c#, a suitable XML object, and a bit of XPath, say
//xsd:element[#ma:displayName='person group'][1]/#name
which should return 'person_x0020_group'
You can then get the Element node for your data, e.g. something along the lines of
Dim cxn As Office.CustomXMLNode
Set cxn = cxp.SelectSingleNode("//*[name()='person_x0020_group'][1]")
Or you may find it's preferable to get the namespace Uri of the Elements in this Custom XML Part and use that to help you locate the correct node. The name is a long hex string assigned by SharePoint. You can get it from the Schema XML using, e.g.
//xsd:schema[1]/#targetNamespace
Once you have your node, you would use the known structures (i.e. the ones you have found in the Schemas) to get/modify/create child nodes as required.
of course you can. You should use the SharePoint Client-side Object model (CSOM) to manipulate SharePoint data from a location away from the server. The only thing you will need is the URL of your SharePoint site.
You can then connect through CSOM like this:
ClientContext context = new ClientContext("SITEURL");
Site site = context.Site;
Web web = context.Web;
context.Load(site);
context.Load(web);
context.ExecuteQuery();
See here an example to set a single user field:
First get the ID of the user through ensuring the username
u = context.Web.EnsureUser(UserOrGroupName);
context.Load(u);
context.ExecuteQuery();
To set the value, you can use this string format:
userid;#userloginname;#
To set the field use this:
item[myusercolumn] = "userid;#userloginname;#";
item.Update();
context.ExecuteQuery();
To set a multi user field, you can use the same code, just use ;# to concat the different usernames, such as:
item[myusercolumn] = "userid1;#userloginname1;#userid2;#userloginname2;#userid3;#userloginname3;#";
item.Update();
context.ExecuteQuery();
Hope this helps

ServiceNow XML Web Service - node naming

I'm creating an app that works with ServiceNow (custom reporting tool)
It's configured to use demo12 and XML service described here.
When i made this request
https://demo12.service-now.com/incident_list.do?XML&sysparm_query=opened_at%3E2012-04-17%2000:00:00%5Eopened_at%3C2012-04-18%2000:00:00%5E&sysparm_view=
in response XML i see not only <incident> nodes, but also <u_zprototype_incidents>
XPath to get node names is
distinct-values(/xml/*/name(.))
and result is (user-friendly formatted)
<XdmValue>
<XdmAtomicValue>u_zprototype_incidents</XdmAtomicValue>
<XdmAtomicValue>incident</XdmAtomicValue>
</XdmValue>
not sure, if this is how it should be displayed.
Is there any other way (extra URI param, etc.) to get valid XML (only <incident> nodes) ?
I know that i can use /xml/*[contains(name(.),'incident')][sys_id='my GUID'] to get needed nodes. but i think it consume more CPU time than just /xml/incident[sys_id='my GUID'].
Any ideas?
For what it's worth, there's something atypical on that demo12 site. There are not supposed to be parent elements named "u_zprototype_incidents" by default. A custom table was created, extending the "incident" table, named "u_zprototype_incidents".
If you want to limit yourself ONLY to records in the base "incident" table, I would suggest that you simply add a new filter for "sys_class_name=incident". Giving you this URL:
https://demo12.service-now.com/incident_list.do?XML&sysparm_query=opened_at%3E2012-04-17%2000:00:00%5Eopened_at%3C2012-04-18%2000:00:00%5E^sys_class_name=incident&sysparm_view=
...With that you can use /xml/incident[sys_id='my GUID']

XML attribute to indicate overwrite vs. preserve existing value

I could use a little guidance on working with XML.
I'm working on a several XML interop specifications which will be used to create or update data in our system. I'd like to provide a way for my customers to specify which elements they want to maintain via automation using the XML spec vs. manually updating the records after the fact. The quick back story to this is that our customers have upstream applications that can supply some but typically not all of the data required by our application so they will typically be sending us partial information for a record and then maintaining the rest in our app.
So assuming a simple XML:
<Data>
<Element1 />
<Element2 />
<Element3 />
</Data>
Customer A may supply element 1 and 2 but not element 3 so they would want to configure 3 to persist values between updates but for 1 and 2 to always overwrite with the new value.
Customer B may supply element 1, 2 and 3 and want to configure to always overwrite all 3 elements.
So I guess what I'm really looking for with Customer A is something like this:
<Data>
<Element1>data1</Element1>
<Element2>data2</Element2>
<Element3 overwriteExistingData="false"></Element3>
</Data>
I understand XML to a degree but have found it very easy to create an XML schema by simply creating C# class, doing some decorating with attributes if necessary and then generating a schema with xsd.exe and calling it a day.
Things that I'm unsure of are
- how to distinguish between a null value and something that the customer didn't have a value for
- the best way to handle these partial data situations where I'm not going to get a complete replacement record for every update.
I'm sure I am missing the boat to some degree but I really like the simplicity of having a class definition and telling the customer to give me XML that allows me to just type one line of code to deserialize the XML into my C# object.
Any guidance would be much appreciated

XML tag name being overwritten with a type defined

We are communicating with a 3rd party service using via an XML file based on standards that this 3rd party developed. They give us an XML template for each "transaction" and we read it into a DataSet using System.Data.DataSet.ReadXML, set the proper values in the DataSet, and then write the XML back using System.Data.DataSet.WriteXML. This process has worked for several different files. However, I am now adding an additional file which requires that an integer data type be set on one of the fields. Here is a scaled down version:
<EngineDocList>
<DocVersion>1.0</DocVersion>
<EngineDoc>
<MyData>
<FieldA></FieldA>
<FieldB></FieldB>
<ClientID DataType="S32"></ClientID>
</MyData>
</EngineDoc>
</EngineDocList>
When I look at the DataSet created by my call to ReadXML to this file, the MyData table has columns of FieldA, FieldB, and MyData_ID. If I then set the value of MyData_ID and then make the call to WriteXML, the export XML has no value for ClientID. Once again, if I take a way the DataType, then I do have a ClientID column, I can set it properly, and the exported XML has the proper value. However, the third party requires that this data type be defined.
Any thoughts on why the ReadXML would be renaming this tag or how I could otherwise get this process to work? Alternatively, I could revamp the way we are reading and writing the XML, but would obviously rather not go down this path although these suggestions would also be welcome. Thanks.
I would not do this with a DataSet. It has a specific focus on simulating a relational model. Much XML will not follow that model. When the DataSet sees things that don't match it's idea of the world, it either ignores them or changes them. In neither case is it a good thing.
I'd use an XmlDocument for this.

Categories