Adding dynamic redirects. Fix for #793

Fix for #793 

*Do not merge this yet!!!!*

This change removes all static binding redirects in app.config, and introduces dynamic redirects in core. The way this is done is by listening to the AssemblyResolve event on the AppDomain, and loading / returning the assembly to redirect to. It turns out using this technique you can swap on the fly and give a different assembly than what was requested.

At startup, it builds a map in map in memory based on the assemblies that scriptcs ships with ensuring that any requests for the same names will be redirected to it's version. It also ensures that any packages in the scriptcs packages folder take precedence over what the script pack / packages assemblies were actually compiled against.

Assembly black magic at its finest....

It appears to work so far :-)

This does not fix the Service Stack script pack issue, as there appears to be no magical fix for that other than updating the script pack. However, I was able to get the old Web Api script pack to work with the latest Web Api (see comments)


Interesting, I was thinking of the scenario where all get added once before
execution not continually.

In those instances I imagine you would really want to run in a separate app
domain and start fresh. Just adding on is brittle for example if you have a
newer assembly and if has been resolved already, it won't be unified. But
if it hasn't it will.

This is very brittle. I would rather say this is not supported.

As everything is lazy, you can feed it everything up front. If someone
needs to change the set, reset the app domain.

I am going to add the behavior I deacon ex for now. If will freeze after
the first resolve.

On Thursday, July 24, 2014, Adam Ralph <notifications@github.com> wrote:

> In src/ScriptCs.Core/AppDomainAssemblyResolver.cs:
>
> > +
> > +        public void AddAssemblyPaths(IEnumerable<string> assemblyPaths)
> > +        {
> > +            foreach (var assemblyPath in assemblyPaths)
> > +            {
> > +                var info = new AssemblyInfo {Path = assemblyPath};
> > +                var name = AssemblyName.GetAssemblyName(assemblyPath);
> > +                info.Version = name.Version;
> > +
> > +                AssemblyInfo foundInfo = null;
> > +                var found = _assemblyInfoMap.TryGetValue(name.Name, out foundInfo );
> > +
> > +                if (!found || foundInfo.Version.CompareTo(info.Version) < 0)
> > +                {
> > +                    _logger.DebugFormat("Mapping Assembly {0} to version:{1}", name.Name, name.Version);
> > +                    _assemblyInfoMap[name.Name] = info;
>
> That's probably good enough, but AppDomain.CurrentDomain.AssemblyResolve
> will only fire after some assembly has failed to resolve. This is why a
> host may see suprising behaviour if they are running more than one script
> and adding some specific assemblies before the execution of each one. I.e.
> it may succeed for the first n scripts but then suddenly fail on n+1
> because an assembly falied to resolve during execution of script n.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/scriptcs/scriptcs/pull/798/files#r15343484>.
>