Скажем, у меня есть интерфейс и класс, определенные ниже.
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;
}
}