.net - 如何从接口动态创建模拟对象?
问题描述
假设我有下面定义的接口和类。
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 容器,对吗?没什么大不了。
但是假设我有几十个服务和 Web 应用程序都依赖于MyInterface
. 它们都MyImplementation
通过 DI 使用,我想更改MyInterface
. 我必须更新和重新部署每个应用程序,这是可行的,但有点痛苦。
我想要完成的是将依赖项封装在服务中并开发一些中间件,这些中间件会自动模拟客户端上的指定接口,将对那个接口的调用转换为 Web 请求(可能是通过反射),将该 Web 请求发送到服务,该服务会将 Web 请求转换回同一接口上的方法调用(同样,可能是通过反射),并调用真正的实现 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;
}
}
解决方案
推荐阅读
- android - Android系统横屏竖屏控制
- yii2 - Yii2 中的 Oracle OCI-Lob 和 ActiveRecord
- c++ - myfunc 没有函数模板的实例
- c++ - 单链表上的冒泡排序最糟糕的时间复杂度?
- go - 我试图测试我用 Go 编写的代码,但我不断收到这个错误,我不知道为什么
- python-3.x - Python - 从一个类中获取列表/字典,在另一个类中进行修改并将修改值返回给第一类
- spring-boot - 在 Spring Boot WebSocket 应用程序中为用户生成唯一的 URL 后缀
- php - 如何在 URL 中获取 Id 以使用 Symfony 5 显示评论?
- laravel - 带循环的轮播组件
- javascript - 简化嵌套对象并找到特定属性的总和