Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 动态更改函数的调用方_C#_.net_Caching - Fatal编程技术网

C# 动态更改函数的调用方

C# 动态更改函数的调用方,c#,.net,caching,C#,.net,Caching,我已经在我的应用程序的DAL中实现了2个提供者 一个是Redis缓存提供程序,另一个是数据库提供程序 public class CacheProvider : IProvider { public List<int> GetCustomerIds() { return cache.GetCustomerIds(); } } public class DBProvider : IProvider { public List<int

我已经在我的应用程序的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();
    }
}
公共类缓存提供程序:IProvider
{
公共列表getCustomerId()
{
返回cache.getCustomerId();
}
}
公共类DBProvider:IProvider
{
公共列表getCustomerId()
{
返回db.getCustomerId();
}
}
我已经为这些实现了一个接口

public interface IProvider
{
     List<int> GetCustomerIds();
}
公共接口IProvider
{
列出getCustomerID();
}
我有以下情况

如果缓存函数在尝试执行时以某种方式出现故障或缓存过期,我希望后退并调用该函数的db版本

将实现许多函数,因此我考虑创建一个网关,其中所有函数都将作为参数传递,如果失败,则返回到db版本

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

    if method fails, run method from db
    myMethodName()
}
公共列表运行方法(Func myMethodName)
{
//从缓存运行方法
myMethodName()
若方法失败,则从db运行方法
myMethodName()
}

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

虽然我不确定性能,但通过一些思考,这是可能的

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

网关不必实现IDataProvider,但这样做会使工作更轻松,因为网关上的方法需要具有与提供者上要调用的方法相同的签名

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

Execute()方法是一种快速连接一切的方法,我们可以每次调用它,让它处理与IDataProviders上的方法的匹配,然后在失败时重新尝试

出于测试目的,我创建了一种强制第一个(缓存提供程序)失败的方法


你基本上回答了你自己的问题!通常我会在我的客户端/前端之间有一个服务层,这将检查对象或对象集合是否在缓存中,如果不在缓存中,我将从db获取并填充缓存。你说的失败是什么意思?失败是指如果缓存出现故障或遇到错误。非常感谢你的回答,艾伦,我会在当天结束之前实施,我会回复你的
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);

    }
}