动态更改函数的调用方
本文关键字:调用 函数 动态 | 更新日期: 2023-09-27 17:57:20
我已经在我的应用程序的 DAL 中实现了 2 个提供程序。
一个是 Redis 缓存提供程序,另一个是数据库提供程序。
public class CacheProvider : IProvider
{
public List<int> GetCustomerIds()
{
return cache.GetCustomerIds();
}
}
public class DBProvider : IProvider
{
public List<int> GetCustomerIds()
{
return db.GetCustomerIds();
}
}
我已经为这些实现了一个接口
public interface IProvider
{
List<int> GetCustomerIds();
}
我有以下情况。
如果缓存函数在尝试执行时以某种方式失败或缓存过期,我想回退并调用函数上的数据库版本。
将实现许多函数,所以我正在考虑创建一个网关,其中所有函数都将作为参数传递,如果失败则回退到数据库版本
public List<int> RunTheMethod(Func<int> myMethodName)
{
// Run method from cache
myMethodName()
if method fails, run method from db
myMethodName()
}
有没有办法实现这种功能?我知道我可能必须实现其中的一些方法,因为参数会有所不同。
有可能,通过一些反思来制作这项工作,尽管我不确定性能
我们有 IDataProvider,它由缓存提供程序、数据库提供程序和"网关"实现(我在 IDataProvider 中包含 3 种方法来显示不同返回/参数和重载的示例)
网关不必实现 IDataProvider,但这样做会使生活更容易一些,因为网关上的方法需要与提供程序上调用的方法具有相同的签名。
网关包含 IDataProvider 的列表,对于每个调用,它都会向下浏览列表并尝试执行。它返回第一个成功,如果没有成功,则引发异常。
Execute<>() 方法是一种将所有内容连接起来的快速方法,我们可以每次调用它,让它处理与 IDataProviders 上的方法匹配并在失败时重试。
出于测试目的,我创建了一种方法来强制第一个(缓存提供程序)失败。
interface IDataProvider {
List<int> Method1();
List<string> Method2(string parameter1);
List<string> Method2(string parameter1, string parameter2);
}
class DataProvider1 : IDataProvider {
private readonly string[] Strings = { "A", "B", "C" };
private bool _callFails;
public DataProvider1(bool callFails) {
_callFails = callFails;
}
public List<int> Method1() {
if (_callFails) {
throw new Exception();
}
return new List<int>(){1,2,3};
}
public List<string> Method2(string parameter1) {
if (_callFails) {
throw new Exception();
}
return Strings.Select(s => s + parameter1).ToList();
}
public List<string> Method2(string parameter1, string parameter2) {
if (_callFails) {
throw new Exception();
}
return Strings.Select(s => s + parameter1 + parameter2).ToList();
}
}
class DataProvider2 : IDataProvider {
private readonly string[] Strings = { "D", "E", "F" };
public List<int> Method1() {
return new List<int>(){4,5,6};
}
public List<string> Method2(string parameter1) {
return Strings.Select(s => s + parameter1).ToList();
}
// overload
public List<string> Method2(string parameter1, string parameter2) {
return Strings.Select(s => s + parameter1 + parameter2).ToList();
}
}
class Gateway : IDataProvider {
private readonly List<IDataProvider> _dataProviders;
public Gateway(IEnumerable<IDataProvider> dataProviders) {
_dataProviders = new List<IDataProvider>(dataProviders);
}
public List<int> Method1() {
return Execute<List<int>>();
}
public List<string> Method2(string parameter1) {
return Execute<List<string>>(parameter1);
}
public List<string> Method2(string parameter1, string parameter2) {
return Execute<List<string>>(parameter1, parameter2);
}
private T Execute<T>(params object[] parameters) {
StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
var methodInfo = typeof(IDataProvider).GetMethod(methodBase.Name, methodBase.GetParameters().Select(p => p.ParameterType).ToArray());
var index = 0;
while (index < _dataProviders.Count) {
try {
return(T)methodInfo.Invoke(_dataProviders[index], parameters);
} catch (Exception) {
index++;
}
}
throw new Exception("None of the methods succeeded");
}
}
单元测试
[TestClass]
public class DataProviderFixture {
#region Create
private Gateway Create(bool firstCallFails = false) {
return new Gateway(new IDataProvider []{
new DataProvider1(firstCallFails),
new DataProvider2()});
}
#endregion
[TestMethod]
public void ExecuteNoProblems() {
var gateway = Create();
var numbers = gateway.Method1();
CollectionAssert.AreEqual(new[] { 1, 2, 3 }, numbers);
var letters = gateway.Method2("1");
CollectionAssert.AreEqual(new[] { "A1", "B1", "C1" }, letters);
letters = gateway.Method2("1", "a");
CollectionAssert.AreEqual(new[] { "A1a", "B1a", "C1a" }, letters);
}
[TestMethod]
public void ExecuteFirstCallFails() {
var gateway = Create(true);
var numbers = gateway.Method1();
CollectionAssert.AreEqual(new[] { 4, 5, 6 }, numbers);
var letters = gateway.Method2("2");
CollectionAssert.AreEqual(new[] { "D2", "E2", "F2" }, letters);
letters = gateway.Method2("1", "b");
CollectionAssert.AreEqual(new[] { "D1b", "E1b", "F1b" }, letters);
}
}