How to Spin up a new app domain in an SSIS Script Task for a Dynamically Loaded Assembly

Viewed 177

I have built an assembly that make calls to the ClosedXML library. Everything works perfectly until I have to export large Excel files, then things go south and I receive the following exception:

System.IO.IsolatedStorage.IsolatedStorageException: Unable to determine the identity of domain.
   at System.IO.IsolatedStorage.IsolatedStorage._GetAccountingInfo(Evidence evidence, Type evidenceType, IsolatedStorageScope fAssmDomApp, Object& oNormalized)
   at System.IO.IsolatedStorage.IsolatedStorage.GetAccountingInfo(Evidence evidence, Type evidenceType, IsolatedStorageScope fAssmDomApp, String& typeName, String& instanceName)
   at System.IO.IsolatedStorage.IsolatedStorage._InitStore(IsolatedStorageScope scope, Evidence domainEv, Type domainEvidenceType, Evidence assemEv, Type assemblyEvidenceType, Evidence appEv, Type appEvidenceType)
   at System.IO.IsolatedStorage.IsolatedStorage.InitStore(IsolatedStorageScope scope, Type domainEvidenceType, Type assemblyEvidenceType)
   at System.IO.IsolatedStorage.IsolatedStorageFile.GetStore(IsolatedStorageScope scope, Type domainEvidenceType, Type assemblyEvidenceType)
   at MS.Internal.IO.Packaging.PackagingUtilities.ReliableIsolatedStorageFileFolder.GetCurrentStore()...

So, after some research, I found some info on how to address the above exception, which is detailed in the questions below:

EPPlus 2.9.0.1 throws System.IO.IsolatedStorage.IsolatedStorageException when trying to save a file bigger than ~1.5 MiB from a SSIS package

Unable to determine the identity of domain using System.IO.Packaging

Isolated Storage Exception: Unable to determine the identity of domain

As suggested in the answers to the questions above, I made sure that all of the classes in my custom assembly inherit from MarshalByRefObject and are marked as Serializable. I have also added a custom assembly ResolveEventHandler.

Saying that, because I am loading the assembly from a file at runtime (this is done because I do not have the option of adding it to the GAC), I am having trouble figuring out how to correctly implement the new domain and its resolve event. When I execute the code below, I recieve following error: repository.GetWorkFlows() is called:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ''System.MarshalByRefObject' does not contain a definition for 'GetWorkFlows''

Code:

using System;
using System.Data.SqlClient;
using System.Reflection;

namespace ST_1736b3ab1e134250a6c05bffb44d449f
{

    [Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
    public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
    {

        public Assembly CustomResolveEventHandler(object sender, ResolveEventArgs args)
        {
            Assembly assembly = null;
            try
            {
                assembly = LoadAssembly(args, assembly);
            }
            catch (Exception exception)
            {
                Dts.Log(exception.ToString(), 0, new byte[0]);
                throw;
            }

            Dts.Log("Exiting CurrentDomain_AssemblyResolve", 0, new byte[0]);
            return assembly;
        }

        public Assembly LoadAssembly(ResolveEventArgs args, Assembly assembly)
        {
            Dts.Log(string.Format("Entering CurrentDomain_AssemblyResolve with args.Name [{0}]", args.Name), 0, new byte[0]);
            string referencedAssembliesFilePath = Dts.Variables["$Package::referencedAssembliesFilePath"].Value.ToString();

            var assemblyName = string.Format("{0}.dll", args.Name.Split(',')[0]);
            Dts.Log("Assembly Name: " + assemblyName, 0, new byte[0]);

            var assemblyFilePath = System.IO.Path.Combine(referencedAssembliesFilePath, assemblyName);
            if (System.IO.File.Exists(assemblyFilePath))
            {
                Dts.Log(string.Format("Currently Loading [{0}]", assemblyFilePath), 0, new byte[0]);
                assembly = Assembly.LoadFile(assemblyFilePath);
            }
            else
            {
                Dts.Log(string.Format("Could Not Find Assembly File [{0}].", assemblyFilePath), 0, new byte[0]);
            }

            return assembly;
        }

        public void Main()
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory.ToString();
            Evidence evidence = new Evidence();
            evidence.AddHostEvidence(new Zone(System.Security.SecurityZone.MyComputer));
            AppDomain domain = AppDomain.CreateDomain("SeparateDomain", evidence, setup);
            separateDomain.AssemblyResolve += new ResolveEventHandler(CustomResolveEventHandler);

            const string referencedAssemblyName = "SSISDataExportManager";
            string query = Dts.Variables["$Package::queryGetDataExportWorkFlowTasks"].Value.ToString();

            dynamic repository = separateDomain.CreateInstanceAndUnwrap(referencedAssemblyName, referencedAssemblyName + "." + "DataExportManagerRepository");  

            using (SqlConnection connection = (SqlConnection)Dts.Connections["DbConnection"].AcquireConnection(Dts.Transaction))
            {

                if (connection.State == System.Data.ConnectionState.Closed) connection.Open();

                //***Exception thrown here***
                dynamic workflows = repository.GetWorkFlows(connection, query, 60);

                dynamic factory = separateDomain.CreateInstanceAndUnwrap(referencedAssemblyName, "DataExportWorkflowSessionFactory");

                dynamic session = factory.CreateSession(workflows);

                session.Execute(connection);

            }

            Dts.TaskResult = (int)ScriptResults.Success;

        }

        #region ScriptResults declaration
        enum ScriptResults
        {
            Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
            Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
        };
        #endregion

    }
}

I assume that this is because I need to explicitly cast to the types in my custom assembly, but again, I cannot do this due to it begin dynamically loaded.

What am I missing? Is there another way to do this, or is this even possible?

0 Answers
Related