SSIS Script Task - No Interface Supported - c#

Purpose: I have an SSIS Solution with various packages, one set of these packages is currently created with a package per table due to them having different structures. The aim is to create a template package (done), update variables/table names (done), reinitialise and re-map columns, execute package for each table.
Problem: So, as you can tell, I'm up to the point that i need to reinitialise but I am unable to get to the data flow task and keep getting this error:
No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))
When I run this code:
//// Get Data Flow task
TaskHost tHost = pkgOut.Executables[0] as TaskHost;
MainPipe dataFlowTask = (MainPipe)tHost.InnerObject;
The code that I am using is below:
public void Main()
{
// Set Project Variables
List<string> pkgVars = new List<string>
{
#"pPR_SSIS_Catalog_Server",
#"pPR_SSIS_Catalog_Project",
#"pPR_SSIS_Catalog_Folder"
};
// Get Package
String pkgLocation = #"C:\PATH\TO\TEMPLATE\PACKAGE\a_Load_SAP_Template.dtsx";
Application app = new Application();
Package pkgIn = app.LoadPackage(pkgLocation, null);
String pkgName = "DynamicPackage";
// Add Connections (cos they're project connections and aren't stored in package)
ConnectionEnumerator allConns = Dts.Connections.GetEnumerator();
while ((allConns.MoveNext()) && (allConns.Current != null))
pkgIn.Connections.Join(allConns.Current);
// Convert new package to XML so we can cheat and just do a find and replace
XmlDocument pkgXML = new XmlDocument();
pkgIn.SaveToXML(ref pkgXML, null, null);
// Replace strings
// Set SAP table
String pkgStr = pkgXML.OuterXml.ToString();
pkgStr = pkgStr.Replace("#SAP_SRC_TABLE#", "MyF-ingSAPTables"); // Replace SAP Table Name
// Set Project Variables references == values -- REMEMBER TO CHECK FOR INT PARAMS zzz
foreach (string var in pkgVars)
pkgStr = pkgStr.Replace(#"#[$Project::" + var + #"]", #"""" + Convert.ToString(Dts.Variables[var].Value) + #"""");
// Convert back to XML
XmlDocument newXML = new XmlDocument();
newXML.LoadXml(pkgStr);
Package pkgOut = new Package();
pkgOut.LoadFromXML(newXML, null);
//// Get Data Flow task
TaskHost tHost = pkgOut.Executables[0] as TaskHost;
MainPipe dataFlowTask = (MainPipe)tHost.InnerObject; // THIS IS WHERE THE CODE ERRORS
new Application().SaveToXml(String.Format(#"D:\PATH\TO\SAVE\LOCATION\{0}.dtsx", pkgName), pkgOut, null);
Dts.TaskResult = (int)ScriptResults.Success;
}
I have tried:
Removing everything but the data flow
Recreated a blank data flow
Recreated the package
Made a package in the script and created the data flow (this works but when i open the package, the data flow does not appear...might have added it wrong but it can see it when I run the TaskHost section)
I am not sure what else I can try, I have seen information that I might need to re-register some .dlls but I do not have admin access and I'd prefer not to go through the hassle for something where I do not know whether it will work.
Any help would be appreciated.
Thanks,

Fixed, was due to version difference in the .DLL that was being loaded. Loaded an older version and all went through fine.

Related

URL absolute path not working with Linq To Excel on server. c#

I'm using Linq to Excel library for reading excel tables. Until now, it was working good locally, the method ExcelQueryFactory gets the route of the excel by this way:
var book = new ExcelQueryFactory(#"C:\data.xls");
Now, I would like to use it online on a Rest Api, the POST used for uploading the Excel to the web api is the following:
[HttpPost]
[Route("Upload")]
public Task<HttpResponseMessage> UploadFile() {
List<string> savedFilePath = new List<string>();
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string rootPath = HttpContext.Current.Server.MapPath("~/UploadedFiles");
var provider = new MultipartFileStreamProvider(rootPath);
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsCanceled || t.IsFaulted)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}
foreach (MultipartFileData item in provider.FileData)
{
try
{
string name = item.Headers.ContentDisposition.FileName.Replace("\"", "");
string newFileName = Guid.NewGuid() + Path.GetExtension(name);
Debug.WriteLine(item.LocalFileName);
File.Move(item.LocalFileName, Path.Combine(rootPath, newFileName));
Uri baseuri = new Uri(Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.PathAndQuery, string.Empty));
//RELATIVE PATH
string fileRelativePath = "~/UploadedFiles/" + newFileName;
//LeerExcel(fileRelativePath);
//ABSOLUTE PATH
Uri fileFullPath = new Uri(baseuri, VirtualPathUtility.ToAbsolute(fileRelativePath));
savedFilePath.Add(fileFullPath.ToString());
//LeerExcel(savedFilePath[0]);
}
catch (Exception ex)
{
string message = ex.Message;
}
}
// string rutaFin = "~" + savedFilePath[0];
// string rest = rutaFin.Replace("http://localhost:56618", "");
// LeerExcel(rest);
return Request.CreateResponse(HttpStatusCode.Created, savedFilePath);
});
return task;
}
So, by choosing the excel manually, neither the absolute path or relative path on the server work for the ExcelQueryFactory string route.
The routes get by this method are the following:
ABSOLUTE:
http://localhost:56618/UploadedFiles/9a27e785-e486-4807-8a80-7abb9b940d8b.xls
And the relative:
/UploadedFiles/9a27e785-e486-4807-8a80-7abb9b940d8b.xls
Is possible to use by the way I want to? During the server is online, the obtained absolute path is accesible, so if I access to that URL, the file is downloaded.
the problem is solved:
As I said, I thought only this library worked locally, but it wasn't.
Since Microsoft has launched a new big update, many people have notice some problems when using Database engine, “Unexpected error from external database driver (1). (Microsoft JET Database Engine)” after applying October security updates.
First, I created a Fake.txt file on /UploadedFiles, folder that is located on the project repository and I give it permission to be always copied, as follows:
Fake.txt properties
With this file, I´m achieving that UploadedFiles folder is copied every time I run the server.
Next step:
Due to Microsoft big update, the recomendation is to "download and install the Microsoft Access Database Engine 2010 Redistributable, and then modify the DB connection strings in Microsoft Excel to use ACE as a provider. Example: Change Provider=Microsoft.Jet.OLEDB.4.0 to Provider=Microsoft.ACE.OLEDB.12.0."
I only have downloaded and install that file, but during this day, 3 new windows updates were installed, so, I dont know if this 3 updates are related with the solution of this problem.
The updates are:
Update 1
Update 2
Update 3
After installing the 2010 database engine version file, I changed the excel extension from .xls to .xlsx and now all work.

Replacing a file with a new file of the same name but different content in TFS via C#

i´m currently working on a programm which updates templates on our companies Team Foundation Server. I am having those new templates locally on my disk and want to replace the existing ones on the server. I was trying different approaches and this is my newest version. The problem is that either
the new file is "in use" when accessing it through coding in c#(while not in use when i try to replace it in runtime using the normal explorer).
the replacement is not appearing in the pending changes, the pendingChanges array is initial.
using (var tfs = TeamFoundationServerFactory.GetServer("myserver"))
{
var versionControlServer = tfs.GetService(typeof(VersionControlServer)) as VersionControlServer;
// Create a new workspace for the currently authenticated user.
var workspace = versionControlServer.CreateWorkspace("Temporary Workspace", versionControlServer.AuthorizedUser);
try
{
// Check if a mapping already exists.
var workingFolder = new WorkingFolder("$serverpath", #"c:\tempFolder");
// Create the mapping (if it exists already, it just overides it, that is fine).
workspace.CreateMapping(workingFolder);
workspace.Get(VersionSpec.Latest, GetOptions.GetAll);
string[] paths = new string[1];
paths[0] = "test.pdf";
workspace.PendEdit(paths, RecursionType.Full, null, LockLevel.None);
// Go through the folder structure defined and create it locally, then check in the changes.
CreateFolderStructure(workspace, workingFolder.LocalItem);
// Check in the changes made.
int a = workspace.CheckIn(workspace.GetPendingChanges(), "This is my comment");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// Cleanup the workspace.
workspace.Delete();
// Remove the temp folder used.
Directory.Delete(#"C:\tempFolder", true);
}
}
}
static void CreateFolderStructure(Workspace workspace, string initialPath)
{
workspace.PendDelete("$serverpath/test.pdf", RecursionType.None);
File.Copy(#"C:\test\testnew.pdf", #"C:\tempfolder\test", true);
workspace.PendAdd(#"C:\tempfolder\test.pdf");
}
I found a solution to the problem. The workspace which was used by "authorizedUser" was obviously not enough.. I found out that a need a TeamFoundationIdentity to do it. Here is a guide on how to fix the issue.
http://blogs.msdn.com/b/taylaf/archive/2010/03/29/using-tfs-impersonation-with-the-version-control-client-apis.aspx

Correct options for Microsoft Sync Framework for Files

I need to do basic one way file synchronization from local to remote server. I tried to use Microsoft Sync Framework, and it works just fine. However, I will need two features which I can not get now:
If file has been deleted on the destination, next synchronization should recreate it from the source
If file has been changed on the destination, next synchronization should replace it from the source
Is it possible to get that by using some options in SyncOrchestrator.Synchronize() function ?
Existing code is based on MSDN article:
public static void SyncFileSystemReplicaOneWay(string sourcePath, string destinationPath)
{
FileSyncProvider sourceProvider = null;
FileSyncProvider destinationProvider = null;
try
{
sourceProvider = new FileSyncProvider(sourcePath);
destinationProvider = new FileSyncProvider(destinationPath);
var agent = new SyncOrchestrator();
agent.LocalProvider = sourceProvider;
agent.RemoteProvider = destinationProvider;
agent.Direction = SyncDirectionOrder.Upload;
var stats = agent.Synchronize();
}
finally
{
if (sourceProvider != null) sourceProvider.Dispose();
if (destinationProvider != null) destinationProvider.Dispose();
}
}
both scenarios will not work out of the box without additional code.
when a sync is done, it detects changes in the source and applies it on destination.
if your case, the change was made on the destination and the source has no way of telling that you deleted a file on destination to include it on change enumeration. if the file on source is modified, you will have better luck as that would be result to a conflict and offer you the opportunity to override the delete on the destination with the file from the source.
same for your second question.
an alternative will be to run detect changes on destination, find out which file were deleted/updated and grabbing those files from the source.

"There is no working folder mapping" error when accessing TFS

I'm working on an application that can create and add items to TFS.
My files get created correctly and in the correct location, however the Workspace.PendAdd method only works for one specific workspace, other workspaces give me the "There is no working folder mapping" error.
The method I am using is PendAdd(string filepath, bool recursive) so I pass in a directory to be added and would expect to add both the directory and its files as a pending add in TFS.
Both workspaces are identical in everything but name, neither have any files currently checked out and neither contain any files.
From my google searches I have found that there may be an issue when adding files to a workspace mapped to $/ but the workspace I am using is mapped to $/TFSFiles/ and so far nothing else seems likely.
The code for my PendAdd method is :
private IEnumerable<PendingChange> PendAddFileToTfs(string newFilename)
{
var previousPendingChanges = new List<PendingChange>(_selectedWorkspace.GetPendingChanges());
var changesAdded = _selectedWorkspace.PendAdd(Path.GetDirectoryName(newFilename), true);
var pendingAdds = new List<PendingChange>(_selectedWorkspace.GetPendingChanges());
var itemsToCheckIn = pendingAdds.Except(previousPendingChanges);
return itemsToCheckIn;
}
The method fails at the _selectedWorkspace.PendAdd line. I debugged and verified it is the correct local path and correct workspace.
Can someone point me in the right direction here?
EDIT:
The _selectedWorkspace variable is set by the user.
I get a list of all available workspaces via this code:
_workspaces = _versionControl.QueryWorkspaces(null, _versionControl.AuthorizedUser,environment.MachineName);
I then show a list of workspaces in a combobox and allow the user to select one.
This calls this method:
public void SetWorkspace(string workspaceName)
{
_selectedWorkspace = _workspaces.FirstOrDefault(ws => ws.Name.Equals(workspaceName));
}
You need to create a local workspace before you can add pendAdd files to TFS. I am not sure where _selectedWorkspace is coming from but it looks like it is not configured properly. Although I don't have a c# version to hand I do have a version in PowerShell that calls c#... should give you the way to go.
function New-TfsTeamProjectRootFolder {
Param(
[Microsoft.TeamFoundation.Client.TfsTeamProjectCollection] $TfsCollection,
[Microsoft.TeamFoundation.Server.ProjectInfo] $TfsTeamProject,
[String] $GlobalEntryValue
)
$TempWorkspaceName = "Create-TfsTeamSourceFolder"
$TempFolder = "c:\temp\$TempWorkspaceName"
$ServerFolder = "$/$($TfsTeamProject.Name)"
$TfsVersionControl = Get-TfsVersionControlServer $TfsCollection
try {
$workspace = $TfsVersionControl.GetWorkspace($TempFolder )
} catch {
$workspace = $TfsVersionControl.CreateWorkspace($TempWorkspaceName);
$workspace.Map($ServerFolder, $TempFolder);
}
$NewFolder = "$TempFolder\$GlobalEntryValue";
try {
$SourceItem = $TfsVersionControl.GetItem("$ServerFolder/$GlobalEntryValue")
} catch {
New-Item -ItemType Directory -Force -Path $NewFolder;
$workspace.PendAdd($NewFolder, $true);
$pendingChanges = $workspace.GetPendingChanges();
$changesetNumber = $workspace.CheckIn($pendingChanges, "Added folder for '$GlobalEntryValue'");
$SourceItem = $TfsVersionControl.GetItem("$ServerFolder/$GlobalEntryValue")
}
$workspace.Delete()
Return $SourceItem
}
Again I am not sure why your code is not working as I think the issue is in the greater context than we can see in the example above.

How do I get build details in a custom workflow activity?

I need to add a custom activity to the default workflow template to increase assembly versions at the earliest point possible in the build process.
What I would like to achieve is to create and map the exact same workspace (that is be created further down in the workflow) inside my custom activity so that I can check out an xml file, increase the version number held within, write it back to the xml file and check the xml file back in.
I'm aware that this workspace will be created later on in the workflow but that will be too late in the build process for what I'm trying to achieve, so instead of moving any of the activities or duplicating them in a position above my custom activity (this should be ok as this workspace will be deleted and recreated again later)
I think the details I need are the BuildDirectory, WorkspaceName and SourcesDirectory. Can anyone tell me how to achieve the creation of the workspace or how obtain this data in code?
the build will be carried out on a build server, and I am using TFS 2010 and c#.
Thanks in advance
I followed the series of blog articles by Ewald Hofman as a primer and created a custom activity that does the check-out, update and check-in of a GlobalAssemblyInfo file that I parse the current version from. My task is inserted at the top of the "Update Drop Location" which is right after it does the "Get the build" portion of the workflow. I just use require the IBuildDetail and a File Mask as arguments from which you can pull out the VersionControlServer to be able to access TFS. My code is below:
protected override string Execute(CodeActivityContext context)
{
// Obtain the runtime value of the input arguments.
string assemblyInfoFileMask = context.GetValue(AssemblyInfoFileMask);
IBuildDetail buildDetail = context.GetValue(BuildDetail);
var workspace = buildDetail.BuildDefinition.Workspace;
var versionControl = buildDetail.BuildServer.TeamProjectCollection.GetService<VersionControlServer>();
Regex regex = new Regex(AttributeKey + VersionRegex);
// Iterate of the folder mappings in the workspace and find the AssemblyInfo files
// that match the mask.
foreach (var folder in workspace.Mappings)
{
string path = Path.Combine(folder.ServerItem, assemblyInfoFileMask);
context.TrackBuildMessage(string.Format("Checking for file: {0}", path));
ItemSet itemSet = versionControl.GetItems(path, RecursionType.Full);
foreach (Item item in itemSet.Items)
{
context.TrackBuildMessage(string.Format("Download {0}", item.ServerItem));
string localFile = Path.GetTempFileName();
try
{
// Download the file and try to extract the version.
item.DownloadFile(localFile);
string text = File.ReadAllText(localFile);
Match match = regex.Match(text);
if (match.Success)
{
string versionNumber = match.Value.Substring(AttributeKey.Length + 2, match.Value.Length - AttributeKey.Length - 4);
Version version = new Version(versionNumber);
Version newVersion = new Version(version.Major, version.Minor, version.Build + 1, version.Revision);
context.TrackBuildMessage(string.Format("Version found {0}", newVersion));
return newVersion.ToString();
}
}
finally
{
File.Delete(localFile);
}
}
}
return null;
}

Categories

Resources