将已编译代码中的生产类的使用替换为模拟类

时间:2014-08-15 18:09:12

标签: c# dll-injection

所以我有生产代码,针对生产dll进行编译。他们访问系统的真实架构。

我正在使用模拟器来模拟这种架构。模拟中的所有类都被命名为相同,并且属于与生产类相同的名称空间。

人们可以使用我的模拟dll对其代码进行草稿测试。

但是,如果他们调用针对生产类编译的现有生产业务逻辑,它将加载现有的生产dll并使用真实的体系结构。

如果人们想要使用我的模拟,但是调用现有的业务逻辑,我必须使用某种注入来覆盖加载生产类的dll。

这可能吗?

举个例子:

我有一个名为Production.dll的dll 在其中有一个类似的类。

namespace Production
{
    public class A { public void Do(); }
}

现在,我有一个名为Simulation.dll的dll具有相同的类和代码。

有人写了一个名为DoA.exe

的程序
public static class Program
{
    public static Main()
    {
        var a = new Production.A();
        a.Do();
    }
}

我想让DoA.exe加载我的模拟dll,但我可能无法从其搜索路径中删除Production.dll。我怎样才能强制它使用Simulation.dll。

2 个答案:

答案 0 :(得分:1)

我想我更了解你的问题。虽然我认为我的原始解决方案更清晰,但这就是如何做到“脏”。

假设你的类模式是这样的(简化):

// assembly: Production.dll (no dependencies)
namespace Production {
  public class Test {
    public void Do() {
      Console.Out.WriteLine("Production");
    }
  }
}

// assembly: Simulation.dll (no dependencies)
namespace Production {
  public class Test {
    public void Do() {
      Console.Out.WriteLine("Simulation");
    }
  }
}

// assembly: Usage.dll (dependency on Production.dll)
namespace Usage {
  public class TestUsage {
    public void Do() {
      new Production.Test().Do();
    }
  }
}

最后将执行覆盖的代码:

// Console application ConsoleApplication.exe
// dependency on Production.dll, Usage.dll and Simulation.dll
namespace ConsoleApplication {
  internal class AssemblyResolver : MarshalByRefObject {
    static internal void Register(AppDomain domain) {
      var resolver = domain.CreateInstanceFromAndUnwrap(
        Assembly.GetExecutingAssembly().Location,
            typeof(AssemblyResolver).FullName) as AssemblyResolver;

      resolver.RegisterDomain(domain);
    }

    private void RegisterDomain(AppDomain domain) {
      domain.AssemblyResolve += ResolveAssembly;
    }

    private Assembly ResolveAssembly(object sender, ResolveEventArgs args) {
      var assemblyName = new AssemblyName(args.Name);
      string name = assemblyName.Name;
      // comment out line below and you'll load "Production" instead
      if (name == "Production") {
        name = "Simulation";
      }
      var fileNames = new[] { name + ".dll", name + ".exe" };
      foreach (string fileName in fileNames) {
        var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
        if (File.Exists(path)) {
          return Assembly.Load(File.ReadAllBytes(path));
        }
      }

      return null;
    }
  }

  class Program {
    static void Main(string[] args) {
      var domain = AppDomain.CreateDomain("Doable", null, new AppDomainSetup {
        DisallowApplicationBaseProbing = true
      });
      AssemblyResolver.Register(domain);
      domain.DoCallBack(() => {
        // writes out "Simulation"
        new Usage.TestUsage().Do();
      });
    }
  }
}

答案 1 :(得分:0)

我使用依赖注入来处理这样的问题:

假设你有像

这样的界面
public interface IDoable {
  void Do();
}
Interfaces.dll中的

而且你的Production.dll与类似:

namespace Production {
  internal class Doable : IDoable {
    public void Do() { Console.Out.WriteLine("Production"); }
  }

  public static class Bootstrapper {
    static void Init(IServiceLocator locator) {
      locator.AddSingleton<IDoable, Doable>();
    }
  }
}

然后你有了像

这样的类的Simulation.dll
namespace Simulation {
  internal class Doable : IDoable {
    public void Do() { Console.Out.WriteLine("Simulation"); }
  }

  public static class Bootstrapper {
    static void Init(IServiceLocator locator) {
      locator.AddSingleton<IDoable, Doable>();
    }
  }
}

然后在您的MainAssembly中,您可以引用它们,并通过配置解析您想要使用的实现(下面的天真示例)。除了配置行之外,您不必关心IDoable来自哪个程序集 - 您只需使用它。

public static Main()
{
    Production.Bootstrapper.Init(ServiceLocator.Instance);
    // or you can use
    // Simulation.Bootstrapper.Init(ServiceLocator.Instance);
    IDoable doable = ServiceLocator.Instance.Resolve<IDoable>();
    doable.Do();
}

实用程序类(使用Enterprise Library中的Microsoft Unity容器):

public interface IServiceLocator {
  void Add<TFrom, TTo>() where TTo : TFrom;
  void BuildUp<T>(T instance);
  void BuildUp(Type type, object instance);
  void AddSingleton<TFrom, TTo>() where TTo : TFrom;
  void AddSingleton<TFrom, TTo>(string name) where TTo : TFrom;
  void AddSingleton(Type from, Type to, string name);
  void AddInstance<T>(T instance);
  T Resolve<T>();
  T Resolve<T>(string name);
}

public class ServiceLocator : IServiceLocator {
  private IUnityContainer m_Container = new UnityContainer();

  public void Add<TFrom, TTo>() where TTo : TFrom {
    m_Container.RegisterType<TFrom, TTo>();
  }

  public void BuildUp<T>(T instance) {
    m_Container.BuildUp<T>(instance);
  }

  public void BuildUp(Type type, object instance) {
    m_Container.BuildUp(type, instance);
  }

  public void AddSingleton<TFrom, TTo>() where TTo : TFrom {
    m_Container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
  }

  public void AddSingleton<TFrom, TTo>(string name) where TTo : TFrom {
    m_Container.RegisterType<TFrom, TTo>(name, new   ContainerControlledLifetimeManager());
  }

  public void AddSingleton(Type from, Type to, string name) {
    m_Container.RegisterType(from, to, name, new ContainerControlledLifetimeManager());
  }

  public void AddInstance<T>(T instance) {
    m_Container.RegisterInstance<T>(instance);
  }

  public T Resolve<T>() {
    return m_Container.Resolve<T>();
  }

  public T Resolve<T>(string name) {
    return m_Container.Resolve<T>(name);
  }

  private static IServiceLocator s_Instance;

  public static IServiceLocator Instance {
    get { return s_Instance; }
  }

  static ServiceLocator() {
    var instance = new ServiceLocator();
    instance.AddInstance<IServiceLocator>(instance);
    s_Instance = instance;
  }
}