动态更改函数的调用者

时间:2014-04-07 10:14:50

标签: c# .net caching

我已在我的应用程序的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();
}

我有以下情况。

如果缓存功能以某种方式失败或缓存在尝试执行时过期,我想退回并在函数上调用db版本。

将会实现许多功能,所以我想创建一个网关,其中所有功能都将作为参数传递,如果失败则回退到db版本

public List<int> RunTheMethod(Func<int> myMethodName)
{
    // Run method from cache
    myMethodName()

    if method fails, run method from db
    myMethodName()
}

有没有办法实现这种功能?我知道我可能必须实施一些这些方法,因为参数会有所不同。

1 个答案:

答案 0 :(得分:2)

虽然我不确定性能,但有可能通过一些反思来完成这项工作

我们有 IDataProvider ,它由缓存提供程序,数据库提供程序和“网关”实现(我在IDataProvider中包含了3个方法,以显示不同返回/参数和重载的示例)

Gateway不必实现IDataProvider,但这样做会让生活变得更轻松,因为Gateway上的方法需要具有与提供者上调用的签名相同的签名。

网关包含一个IDataProviders列表,每次调用它都会通过列表并尝试执行。它返回第一次成功,如果没有成功则抛出异常。

Execute&lt;&gt;()方法是一种快速连接所有内容的方法,我们每次都可以调用它,让它处理与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);

    }
}