Как динамически создать макет объекта из интерфейса? - PullRequest
0 голосов
/ 11 мая 2018

Скажем, у меня есть интерфейс и класс, определенные ниже.

public interface MyInterface
{
    void Method1();
    int Method2(int i);
    string Method3(string s);
}

public class MyImplementation : MyInterface
{
    public void Method1()
    {

    }

    int Method2(int i)
    {
        return i;
    }

    string Method3(string s)
    {
        return s;
    }
}

MyInterface определено в одной библиотеке классов, а MyImplementation определено во второй библиотеке классов.

Теперь скажите, что у меня есть приложение, которое зависит от MyInterface и получает его реализацию посредством внедрения зависимостей. Приложение может просто ссылаться на библиотеку классов реализации и регистрировать MyImplementation с контейнером IoC, верно? Ничего страшного.

Но, скажем, у меня есть десятки сервисов и веб-приложений, все из которых зависят от MyInterface, все они используют MyImplementation через DI, и я хочу изменить эту реализацию MyInterface. Мне пришлось бы обновлять и повторно развертывать каждое приложение, что выполнимо, но немного болезненно.

То, что я пытаюсь выполнить, - это инкапсулировать зависимость в сервисе и разработать некоторое промежуточное программное обеспечение, которое будет автоматически насмехаться над указанным интерфейсом на клиенте, преобразовывать вызовы этого интерфейса в веб-запрос (предположительно через отражение), отправлять этот веб В запросе к сервису сервис конвертирует веб-запрос обратно в вызов метода на том же интерфейсе (опять же, предположительно, через отражение) и вызывает реальную реализацию DI'ed в сервисах. Это позволило бы мне изолировать зависимость от одной службы, которую я могу изменить по своему усмотрению, без необходимости повторного развертывания всех других приложений и служб.

Во-первых, это похоже на проблему, которую кто-то должен был уже решить, но я не могу найти никаких консервативных решений.

Во-вторых, если бы я сам реализовал нечто подобное. Как бы я вообще начал? Похоже, я пытаюсь сделать что-то похожее на Moq или Rhino Mocks, за исключением того, что я не хочу явно определять, что делать для каждого метода. Я хотел бы обработать это автоматически через отражение. Мой сервис должен получить запрос POST с телом, содержащим FQN интерфейса, имя метода и массив параметров, чтобы я мог найти сторону метода и вызвать ее.

public static class IServiceCollectionExtensions
{
    public static IServiceCollection AddMiddlewareClient<T>(this IServiceCollection services)
    {
        var type = typeof(T);
        var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
        var members = type.GetMembers(bindingFlags);

        var mockObject = ???

        foreach(var member in members)
        {
            // Mock each method on the interface with one that sends 
            // a web request to the service.
        }

        services.AddSingleton<T>(mockObject);

        return services;
    }
}

Сервис выглядит примерно так.

public class HomeController : Controller
{
    private IServiceProvider _services;

    public HomeController(IServiceProvider services)
    {
        _services = services;
    }

    [HttpPost("invoke/{typeName}/{memberName}")]
    public object Invoke(string typeName, string memberName, [FromBody]Parameter[] parameters = null)
    {
        var interfaceType = Type.GetType(typeName);

        var args = parameters
        .Select(p =>
        {
            var parameterType = Type.GetType(p.TypeName);
            var parameter = JsonConvert.DeserializeObject(p.Json, parameterType);
            return parameter;
        })
        .ToArray();

        var impl = _services.GetRequiredService(interfaceType);
        var implType = impl.GetType();
        var bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
        var result = implType.InvokeMember(memberName, bindingFlags, null, impl, args);

        return result;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...