Dynamic Flat File Source Path - c#

I'm trying to pass a dynamic file path to a SSIS package variable. I'm setting the ConnectionString value to the variable value in the connection expressions.
So to get started I set the initial variable name value to products.csv and get the error:
Nonfatal errors occurred while saving the package: The name cannot
contain any of the following characters: / \ : [ ] . =
I remove the .csv from the variable name, since I'm passing this value from code anyways - no biggie. But when I pass the appropriate variables and execute the package I get weirdness. I check the db and the expected values from the package are there, but I get a failure status and errors similar to the one I mentioned above.
Below is my code:
GeneralUtilities.ExecutePackage(
new ListDictionary()
{
{"ClientId", Client.Id},
{"FileName", ConfigurationManager.AppSettings["DownloadLocation"] + #"\" + this.OriginalName}
},
"Products.dtsx");
public static void ExecutePackage(ListDictionary variables, string packageName)
{
string pkgLocation;
Package pkg;
Application app;
DTSExecResult pkgResults;
Variables vars;
pkgLocation = ConfigurationManager.AppSettings["PackageLocation"] + packageName;
app = new Application();
using (pkg = app.LoadPackage(pkgLocation, null))
{
vars = pkg.Variables;
foreach (DictionaryEntry variable in variables)
{
vars[variable.Key].Value = variable.Value;
}
pkgResults = pkg.Execute(null, vars, null, null, null);
for(int i=0;i<pkg.Errors.Count;i++)
Console.WriteLine(pkg.Errors[i].Description);
Console.WriteLine(pkgResults.ToString());
}
}
And here's the output:
It's cool that it's being input into the db, but I don't think the Failure message will work. Seems like a package setting or something that is causing a 'false failure'. Can anyone provide help? Thanks.

At this point, I think we need more information. I built out a sample package that imports a CSV; defined 2 variables as indicated (ClientId & FileName); configured the package to use a variable for the CM's ConnectionString property. Finally, I used your your code to run the package. I received no errors.
I was not familiar with the Execute method that takes parameters so I tested your code with both versions of pkg.Execute and the results are the same (you can observe this by saving out the modified package as I do so on the final line and comparing them). Before execution, my variables look like
ClientId: 0
FileName: C:\ssisdata\so\so_matt.csv
RowCount: 0
After execution and saving as so_JeffBorden2.dtsx my variables have been modified as expected.
ClientId: 100
FileName: C:\ssisdata\so\so_matt2.csv
RowCount: 10
private static void so_JeffBorden()
{
string path = #"C:\sandbox\SSISHackAndSlash2008\SSISHackAndSlash2008\so_JeffBorden.dtsx";
ListDictionary variables;
variables = new ListDictionary()
{
{"ClientId", 100},
{"FileName", #"C:\ssisdata\so\so_matt2.csv"}
};
string pkgLocation;
Package pkg;
Application app;
DTSExecResult pkgResults;
Variables vars;
pkgLocation = path;
app = new Application();
using (pkg = app.LoadPackage(pkgLocation, null))
{
vars = pkg.Variables;
foreach (DictionaryEntry variable in variables)
{
vars[variable.Key].Value = variable.Value;
}
pkgResults = pkg.Execute(null, vars, null, null, null);
//pkgResults = pkg.Execute();
for (int i = 0; i < pkg.Errors.Count; i++)
Console.WriteLine(pkg.Errors[i].Description);
Console.WriteLine(pkgResults.ToString());
app.SaveToXml(#"C:\sandbox\SSISHackAndSlash2008\SSISHackAndSlash2008\so_JeffBorden2.dtsx", pkg, null);
}
}
Is there something cut from your sample code that was needed to generate the error? If you run your full code against my sample package, do you encounter errors?
Root cause
The error in the screenshot indicates there is an expression assigning a value to the Name property of an object. Names in SSIS generally follow the .NET rules for naming (connection managers excluded). There are 5 total objects in this package: two variables and a data flow which contains 3 items (I'm not good at math). The variables are not having their name property modified based on the supplied code. The names of the components in the data flow are all the default names which leaves the data flow's name as the prime suspect. Note that the name of the data flow products is an identical match to the value of the variable #[User::FileName] The problem only surfaces when the package executes which is the only time the value of FileName would change.
To test this, in BIDS/SSDT modify the value of variable FileName to be 'doh.txt' and the package should start throwing errors due to bad name.
To resolve this, right click on the data flow and either delete the expression on the Name property or sanitize the string by removing the offending characters in the expression (liberal use of REPLACE will do the trick)

Related

Querying TFS 2015 Source Code Repository DateTime Properties with .Net Framework v4.8 and Visual Studio 2019

I am trying to filter for source control files that were either created or modified within a specific time period on particular Team Foundation Server 2015 branches. I am thus far able to access file properties (e.g. url) with the Microsoft.VisualStudio.Services.WebAPI and Microsoft.TeamFoundation.SourceControl.WebApi libraries with a C# .Net Framework 4.8 Console Application using the GitHttpClient class.
The GetItemsAsync() method of this class returns a list of "GitItems" that contain a "path" property that can be passed as an argument into the System.IO class FileInfo to instantiate an object with the properties I need: CreationTime and LastWriteTime. However, the GitItem objects do not include the full file (blob) path that FileInfo (as well as the class File) needs to generate these properties accurately. The path property only includes the file name (e.g. '/.gitignore'). Therefore, in the code below, the variable lastWriteTime and the CreationTime property both return '12/31/1600 7:00:00 PM,' since the path isn't recognized.
static void Main(string[] args)
{
VssCredentials creds = new VssClientCredentials();
creds.Storage = new VssClientCredentialStorage();
VssConnection connection = new VssConnection(new Uri(teamCollection), creds);
// Get a GitHttpClient to talk to the Git endpoints
GitHttpClient gitClient = connection.GetClient<GitHttpClient>();
// Get data about a specific repository
var repositories = gitClient.GetRepositoriesAsync(teamProject).Result;
GitVersionDescriptor descriptor = new GitVersionDescriptor()
{
VersionType = GitVersionType.Branch,
Version = "develop",
VersionOptions = GitVersionOptions.None
};
foreach (var repository in repositories)
{
var branches = gitClient.GetBranchesAsync(repository.Id).Result;
var items = gitClient.GetItemsAsync(repository.Id, recursionLevel: VersionControlRecursionType.Full, versionDescriptor: descriptor, includeContentMetadata: true).Result;
foreach (var item in items)
{
var fullPath = Path.GetFullPath(item.Path);
FileInfo file = new FileInfo(fullPath);
DateTime lastWriteTime = file.LastWriteTime;
}
Console.WriteLine(repository.Name);
}
}
}
}
According to your code, you are using GitHttpClient.GetItemsAsync method.
public Task<GitItemsCollection> GetItemsAsync(
Guid repositoryId,
string path,
GitVersionDescriptor version,
VersionControlRecursionType recursionLevel,
bool includeContentMetadata,
bool includeLatestChange,
Object userState
)
This will return a server side git path. File info class with LastWriteTime properties
Gets or sets the time when the current file or directory was last written to. This should be a local system path.
That's why the path isn't recognized. Which may return a date kind of '12/31/1600 7:00:00 PM,'
Your question is similar to this VSTS API - repository creation date
Don't think it is possible to get the exact date of the moment the
operation create repo was completed. However, logically the birthday
of the repository is usually considered its first commit date.
If that's what you're looking for, you can achieve your goal with a
usual Git command:
git log -1 --reverse --format="format:%ci"
Besides, you could also get a git commit with detail info through Rest API. Also take a look at this blog, which maybe helpful.

Tfs Check-in using PendAdd: The array must contain at least one element

So I'm having a problem with automating my code to check-in files to TFS, and it's been driving me up the wall! Here is my code:
string location = AppDomain.CurrentDomain.BaseDirectory;
TfsTeamProjectCollection baseUserTpcConnection = new TfsTeamProjectCollection(uriToTeamProjectCollection);
IIdentityManagementService ims = baseUserTpcConnection.GetService<IIdentityManagementService>();
TeamFoundationIdentity identity = ims.ReadIdentity(IdentitySearchFactor.AccountName, #"PROD1\JR", MembershipQuery.None, ReadIdentityOptions.None);
TfsTeamProjectCollection impersonatedTpcConnection = new TfsTeamProjectCollection(uriToTeamProjectCollection, identity.Descriptor);
VersionControlServer sourceControl = impersonatedTpcConnection.GetService<VersionControlServer>();
Workspace workspace = sourceControl.CreateWorkspace("MyTempWorkspace", sourceControl.AuthorizedUser);
String topDir = null;
try
{
Directory.CreateDirectory(location + "TFS");
String localDir = location + "TFS";
workspace.Map("$/Automation/", localDir);
workspace.Get();
destinationFile = Path.Combine(localDir, Name + ".xml");
string SeconddestinationFile = Path.Combine(localDir, Name + ".ial");
bool check = sourceControl.ServerItemExists(destinationFile, ItemType.Any);
PendingChange[] pendingChanges;
File.Move(sourceFile, destinationFile);
File.Copy(destinationFile, sourceFile, true);
File.Move(SecondsourceFile, SeconddestinationFile);
File.Copy(SeconddestinationFile, SecondsourceFile, true);
if (check == false)
{
workspace.PendAdd(localDir,true);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
else
{
workspace.PendEdit(destinationFile);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
and the problem is that whenever it's NEW files (PendEdit works correctly when the files already exist in TFS) that my code is attempting to check in, and it runs through this code:
if (check == false)
{
workspace.PendAdd(localDir,true);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
The files, instead of being in the included changes in pending changes, are instead in the excluded changes like so:
and when the line that actually does the check-in runs, I'll get a "The array must contain at least one element" error, and the only way to fix it is to manually add those detected changes, and promote them to included changes, and I simply can't for the life of me figure out how to do that programatically though C#. If anyone has any guidance on what direction I should take for this, I would really appreciate it! Thank you!
Edit: I've also discovered another way to solve this by reconciling the folder, which also promotes the detected changes, but again the problem is I can't seem to figure out how to program that to do it automatically.
I know that running the visual studio developer command prompt, redirecting to the folder that this mapping is in, and the running "tf reconcile /promote" is one way, but I can only automate that as far as the /promote part, because that brings up a toolbox that a user would have to input into, which defeats the purpose of the automation. I'm at a loss.
Next Edit in response to TToni:
Next Edit in response to TToni:
I'm not entirely sure if I did this CreateWorkspaceParameters correctly (see picture 1), but this time it gave the same error, but the files were not even in the excluded portions. They just didn't show up anywhere in the pending changes (see picture 2).
Check this blog:
The workspace has a method GetPendingChangesWithCandidates, which actually gets all the “Excluded” changes. Code snippet is as below:
private void PendChangesAndCheckIn(string pathToWorkspace)
{
//Get Version Control Server object
VersionControlServer vs = collection.GetService(typeof
(VersionControlServer)) as VersionControlServer;
Workspace ws = vs.TryGetWorkspace(pathToWorkspace);
//Do Delete and Copy Actions to local path
//Create a item spec from the server Path
PendingChange[] candidateChanges = null;
string serverPath = ws.GetServerItemForLocalItem(pathToWorkspace);
List<ItemSpec> its = new List<ItemSpec>();
its.Add(new ItemSpec(serverPath, RecursionType.Full));
//get all candidate changes and promote them to included changes
ws.GetPendingChangesWithCandidates(its.ToArray(), true,
out candidateChanges);
foreach (var change in candidateChanges)
{
if (change.IsAdd)
{
ws.PendAdd(change.LocalItem);
}
else if (change.IsDelete)
{
ws.PendDelete(change.LocalItem);
}
}
//Check In all pending changes
ws.CheckIn(ws.GetPendingChanges(), "This is a comment");
}

How to get the name of the Oid (#Snmp)?

Ok, following the advice of Lex Li and I try to get Oid name using an other lib : #SnmpLib
Here the sample :
public static void Main(string[] args)
{
string oid = ".1.3.6.1.4.1";
IObjectRegistry registry = new ReloadableObjectRegistry(#"C:\Users\Fnizz\Desktop\MIBS_BARCO\");
IObjectTree tree = registry.Tree;
var o = tree.Search(ObjectIdentifier.Convert(oid));
string textual = o.AlternativeText;
Console.WriteLine(textual);
if (o.GetRemaining().Count == 0)
{
Console.WriteLine(o.Definition.Type.ToString());
}
Console.ReadKey();
}
But instead of to get the value .iso.org.dod.internet.private.enterprises I get this one : .iso.3.6.1.4.1
You must use #SNMP MIB Compiler (Compiler.exe) to compile your MIB documents first. If there is any dependency missing, it will tell. Only when all dependencies are there, the Compiler can compile your documents without an error.
The compiler generates *.module files in modules folder. Then you need to put these files (*.module) into C:\Users\Fnizz\Desktop\MIBS_BARCO\ folder.
The object registry is able to load them and perform the name resolution correctly. The object registry does not under MIB documents directly, so feeding it with MIB documents will not work.

How to Create and Execute an SSIS Package on remote sql Server source and destination using EzAPI

I need to create a Console application that can copy a table from one remote sql server instance to another remote sql server instance.
I'm using a library called EzAPI
Both connections (Source and Destinations) and table name will be provided as parameters to the Console application.
Here is my try:
public class OleDBToOleDB : EzSrcDestPackage<EzOleDbSource, EzSqlOleDbCM, EzOleDbDestination, EzSqlOleDbCM>
{
public OleDBToOleDB(Package p) : base(p) { }
public static implicit operator OleDBToOleDB(Package p) { return new OleDBToOleDB(p); }
public OleDBToOleDB(string SrcServer, string SrcDB, string SrcTable,string SrcUser,string SrcPassword, string DstServer, string DstDB, string DstTable,string DstUser,string DstPassword)
{
SrcConn.SetConnectionString(SrcServer, SrcDB);
SrcConn.ServerName = SrcServer;
SrcConn.InitialCatalog = SrcDB;
SrcConn.UserName = SrcUser;
SrcConn.Password = SrcPassword;
Source.Table = SrcTable;
DestConn.SetConnectionString(DstServer, DstDB);
DestConn.ServerName = DstServer;
DestConn.InitialCatalog = DstDB;
DestConn.UserName = DstUser;
DestConn.Password = DstPassword;
Dest.Table = DstTable;
}
}
static void Main(string[] args)
{
OleDBToOleDB p = new OleDBToOleDB("localhost", "TestDB", "Address", "sa", "123", "localhost", "DestDB", "Address", "sa", "123");
p.Execute();
Console.Write(string.Format("Package2 executed with result {0}\n",p.ExecutionResult));
}
The problem with this code is:
It does not create the table on the destination server, so I should
create it manually by myself.
This code runs successfully on the localhost, but when I try to
change the server name to a remote server it raises this error:
Unhandled Exception: System.Runtime.InteropServices.COMException
(0xC020801C): Exception from HRESULT: 0xC020801C
After searching on the web I found that this error means that this error is an Integration services AcquireConnections exception.
So how can I get this code running on a remote sql server instance and have the package create the table on the destination server before transferring the data.
Thank you in advance.
SSIS needs both tabled during the package generation, since it has to get the whole metadata for each column in your tables. You can set the ValidateMetadata property to false, in this case it will not validate it, but you will need to fill all the data by yourself. This is not easy.
I think the most easy thing you can do is:
1)generate the package with local connections and turn off the validation for your destination component
2)now set the new connection strings to your source and destination compoennts
3)run the package
this is a not really clean workaround. This should be usually done with a configurations, but since you do not save your package you can try this as well.
I think it can be also easier to implement your task with SqlBuldCopy without SSIS

IManExt ImportCmd trouble

I have been writing a small application using C# to copy a document into an individuals 'My Documents' folder on our DMS server.
I've beased the code around the listing provided in the 'WorkSite SDK 8: Utilize the IMANEXT2Lib.IManRefileCmd to File New Document Folders' blog.
Using this code in a WinForm application I have no problems copying the file from the source folder into the users DMS 'My Documents' folder.
However if I use the code in a command line application/.dll or any other type of application (other than WinForm) during the copy process I receive the error messages;
1.
Error occurred when try to log the event!
IManExt: Error occurred when try to log the event!
Access is denied.
2.
The document was imported to the database, but could not be added to
the folder.
IManExt: The document was imported to the database, but could not be
added to the folder.
IManExt.LogRuleEventsCmd.1: Error occurred when try to log the event!
IManExt.LogRuleEventsCmd.1: Access is denied.
Error occurred when try to log the event!
-%-
Does anyone know why I'd receiving the 'Access Denied' error messages when using a non-WinForms application to copy documents?
What would I need to do to get around this issue?
Any help would be amazing!
Code in place:
public void moveToDMS(String servName, String dBName, String foldName)
{
const string SERVERNAME = servName; //Server name
const string DATABASENAME = dBName; //Database name
const string FOLDERNAME = foldName; //Matter alias of workspace
IManDMS dms = new ManDMSClass();
IManSession sess = dms.Sessions.Add(SERVERNAME);
sess.TrustedLogin();
//Get destination database.
IManDatabase db = sess.Databases.ItemByName(DATABASENAME);
//Get destination folder by folder and owner name.
IManFolderSearchParameters fparms = dms.CreateFolderSearchParameters();
fparms.Add(imFolderAttributeID.imFolderOwner, sess.UserID);
fparms.Add(imFolderAttributeID.imFolderName, FOLDERNAME);
//Build a database list in which to search.
ManStrings dblist = new ManStringsClass();
dblist.Add(db.Name);
IManFolders results = sess.WorkArea.SearchFolders(dblist, fparms);
if (results.Empty == true)
{
//No results returned based on the search criteria.
Console.WriteLine("NO RESULTS FOUND!");
}
IManDocumentFolder fldr = null;
if (results.Empty == false)
{
//Assuming there is only one workspace returned from the results.
fldr = (IManDocumentFolder)results.ItemByIndex(1);
}
if (fldr != null)
{
// Import file path
string docPath = #"C:\Temp\";
string docName = "MyWord.doc";
// Create an instance of the ContextItems Collection Object.
ContextItems context = new ContextItemsClass();
// Invoke ImportCmd to import a new document to WorkSite database.
ImportCmd impCmd = new ImportCmdClass();
// The WorkSite object you pass in can be a database, session, or folder.
// Depends on in where you want the imported doc to be stored.
context.Add("IManDestinationObject", fldr); //The destination folder.
// Filename set here is used for easy example, a string variable is normally used here
context.Add("IManExt.Import.FileName", docPath + docName);
// Document Author
context.Add("IManExt.Import.DocAuthor", sess.UserID); //Example of a application type.
// Document Class
context.Add("IManExt.Import.DocClass", "BLANK"); //Example of a document class.
//context.Add("IManExt.Import.DocClass", "DOC"); //Example of a document class.
// Document Description (optional)
context.Add("IManExt.Import.DocDescription", docName); //Using file path as example of a description.
// Skip UI
context.Add("IManExt.NewProfile.ProfileNoUI", true);
impCmd.Initialize(context);
impCmd.Update();
if (impCmd.Status == (int)CommandStatus.nrActiveCommand)
{
impCmd.Execute();
bool brefresh = (bool)context.Item("IManExt.Refresh");
if (brefresh == true)
{
//Succeeded in importing a document to WorkSite
IManDocument doc = (IManDocument)context.Item("ImportedDocument");
//Succeeded in filing the new folder under the folder.
Console.WriteLine("New document number, " + doc.Number + ", is successfully filed to " + fldr.Name + " folder.");
}
}
}
}
Just in case this helps someone else.
It seems my issue was the result of a threading issue.
I noticed the C# winform apps I had created were automatically set to run on a single 'ApartmentState' thread ([STAThread]).
Whereas the console applications & class library thread state and management hadn't been defined within the project and was being handled with the default .NET config.
To get this to work: In the console application, I just added the [STAThread] tag on the line above my Main method call.
In the class library, I defined a thread for the function referencing the IMANxxx.dll and set ApartmentState e.g.
Thread t = new Thread(new ThreadStart(PerformSearchAndMove));
t.SetApartmentState(ApartmentState.STA);
t.Start();
In both cases ensuring single 'ApartmentState' thread was implemented set would resolve the issue.

Categories