Castle DynamicProxy - ошибка при создании прокси с использованием GTP, используемого в качестве GTR
Хорошо, теперь я действительно запутался.
Первоначально у меня была эта проблема, которая, согласно постерам, была проблемой с версией Castle.DynamicProxy, которая ILMerged в последнюю библиотеку Rhino.Mocks. Согласно нескольким авторитетам по этому вопросу, он был исправлен в последнем Замке, но эта библиотека не превратила его в новый Rhino.Mocks. Большинство людей говорят: "Просто скачайте исходники Rhino и последнюю версию Castle и создайте свою собственную версию".
Итак, я сделал именно это; Я взял ZIP-архив с источником ствола Rhino из GitHub Айенде, открыл его и собрал. Затем, как хороший маленький TDDer, я создал модульный тест, чтобы убедиться, что мои изменения работали (потому что последняя версия Castle сворачивает DynamicProxy в Core, требуя некоторых существенных изменений ссылок):
[Test]
public void MockOfInterfaceMethodWithInterfaceGTR()
{
var mock = mocks.DynamicMock<ITestRestrictedInterface>();
Assert.NotNull(mock);
Expect.Call(mock.TestMethod(new Object2())).IgnoreArguments().Return(5);
mocks.ReplayAll();
Assert.AreEqual(5, mock.TestMethod(new Object2()));
}
...
internal interface ITestGenericInterface<TRest> where TRest:IObject1
{
int TestMethod<T>(T input) where T : TRest;
}
internal interface ITestRestrictedInterface:ITestGenericInterface<IObject2> { }
internal interface IObject1 { }
internal interface IObject2:IObject1 { }
internal class Object2:IObject2 { }
В результате, когда я запустил собственный производственный код с последним выпущенным Rhino? Ошибка со следующим сообщением:
System.TypeLoadException: метод "TestMethod" для типа "ITestRestrictedInterfaceProxy83ad369cdf41472c857f61561d434436" из сборки "DynamicProxyGenAssembly2, Version=0.0.0.0, Culture= нейтральный, PublicKeyToken=null" пытался неявным образом реализовать метод с интерфейсом слабого параметра - метод пытался неявно внедрить интерфейсный метод с неявным параметром, неявным образом реализующим интерфейсный метод с неявным типом реализации с косвенным параметром метода с неявной реализацией.
... Однако, когда я копирую и вставляю этот тест в прибор в проекте Rhino.Mocks.Tests, без внесения каких-либо изменений в библиотеки, на которые ссылаются, тест PASSES. Я сделал ноль изменений в загруженном источнике. Я внес нулевые изменения в метод испытаний и соответствующие интерфейсы / объекты с обеих сторон. Я создал новую DLL-библиотеку Rhino.Mocks (без слияния IL с библиотеками Castle) и скопировал ее с помощью библиотек Castle обратно в мое производственное решение, повторно запустил тест, и он все еще не работает с тем же сообщением.
WTF?
1 ответ
Я не эксперт по замкам и не гуру компиляторов, но я полагаю, что проблема - это немного магии, которая скрыта внутри сборки RhinoMocks.Tests:
С https://github.com/ayende/rhino-mocks/blob/master/Rhino.Mocks.Tests/TestInfo.cs
using System.Runtime.CompilerServices;
using Rhino.Mocks;
[assembly: InternalsVisibleTo(RhinoMocks.StrongName)]
И для полноты картины RhinoMocks.StrongName определяется как:
/// <summary>
/// Used for [assembly: InternalsVisibleTo(RhinoMocks.StrongName)]
/// Used for [assembly: InternalsVisibleTo(RhinoMocks.NormalName)]
/// </summary>
public static class RhinoMocks
{
/// <summary>
/// Strong name for the Dynamic Proxy assemblies. Used for InternalsVisibleTo specification.
/// </summary>
public const string StrongName =
"DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7";
/// <summary>
/// Normal name for dynamic proxy assemblies. Used for InternalsVisibleTo specification.
/// </summary>
public const string NormalName = "DynamicProxyGenAssembly2";
/// <summary>
/// Logs all method calls for methods
/// </summary>
public static IExpectationLogger Logger = new NullLogger();
}
Я видел аналогичную проблему при использовании Moq, в которой эта проблема задокументирована.
Проблема в том, что DynamicProxy в Castle должен динамически получать новый тип, но не может видеть ваш интерфейс, который является внутренним для вашей сборки. Простое добавление InternalsVisibleTo в DynamicProxyGenAssembly2 к вашей тестовой библиотеке должно решить проблему.