使用Entity Framework从一个数据库故障转移到另一个数据库的一般模式?

时间:2014-05-03 20:31:19

标签: entity-framework database-design

我们有一个企业数据库,可以通过世界各地的许多网站进行复制。我们希望我们的应用程序尝试连接到其中一个本地站点,如果该站点关闭,我们希望它回退到企业数据库。我们在每个数据库操作中都喜欢这种行为。

我们正在使用Entity Framework,C#和SQL Server。

首先,我希望我可以在连接字符串中指定“故障转移伙伴”,但这只适用于镜像数据库环境,而不是这样。我还研究了编写自定义IDbExecutionStrategy。但是这些策略仅允许您指定重试失败的数据库操作的模式。它不允许您以任何方式更改操作,例如将其指向新连接。

那么,除了在我们的许多数据库操作中复制重试逻辑之外,您是否知道处理此类操作的任何良好模式?


2014-05-14更新:

我将详细说明已经提出的一些建议。

我有许多位置代码如下所示:

try
{
    using(var db = new MyDBContext(ConnectionString))
    {
        // Database operations here.
        // var myList = db.MyTable.Select(...), etc.
    }
}
catch(Exception ex)
{
    // Log exception here, perhaps rethrow.
}

有人建议我有一个例程,首先检查每个连接字符串并返回第一个成功连接的连接字符串。这是合理的。但是我看到的一些错误是操作超时,连接工作正常但数据库存在使其无法完成操作的问题。

我正在寻找的是一种模式,我可以用它来封装工作单元,然后说:“在第一个数据库上尝试这个。如果因任何原因失败,请回滚并在第二个数据库上尝试。如果是失败,在第三个等尝试,直到操作成功或你没有更多的数据库。“我很确定我可以自己滚动(如果我这样做,我会发布结果),但我希望可能有一种已知的方法来解决这个问题。

3 个答案:

答案 0 :(得分:0)

如何使用一些依赖注入系统(如autofac)并为新的上下文对象注册工厂 - 它将执行将尝试首先连接到本地的逻辑,如果失败,它将连接到企业数据库。然后它将返回准备好的DbContext对象。该工厂将通过依赖注入系统提供给需要它的所有对象 - 他们将使用它来创建上下文并在不再需要时处理它们。

答案 1 :(得分:0)

"我们希望我们的应用程序尝试连接到其中一个本地站点,如果该站点关闭,我们希望它回退到企业数据库。我们在每个数据库操作中都喜欢这种行为。"

如果您的应用在数据库上严格只读,并且数据一致性对您的应用/用户并非绝对重要,那么只需尝试连接直到找到运营网站即可。正如M.Ali在他的评论中所说的那样。

否则,我建议你不要立即考虑这条线,因为你只是在一条死胡同里跑了90英里/小时。正如Viktor Zychla在他的评论中所说的那样。

答案 2 :(得分:0)

以下是我最终实现的内容,广泛的笔触:

定义名为UnitOfWorkMethod的委托,它将在单个事务中执行数据库上的单个工作单元。它需要一个连接字符串,并且还会返回一个值:

delegate T UnitOfWorkMethod<out T>(string connectionString);
delegate void UnitOfWorkMethod(string connectionString);

定义一个名为ExecuteUOW的方法,该方法将使用一个工作单元和方法尝试使用首选连接字符串执行它。如果失败,它会尝试使用下一个连接字符串执行它:

protected T ExecuteUOW<T>(UnitOfWorkMethod<T> method)
{
    // GET THE LIST OF CONNECTION STRINGS
    IEnumerable<string> connectionStringList = ConnectionStringProvider.GetConnectionStringList();

    // WHILE THERE ARE STILL DATABASES TO TRY, AND WE HAVEN'T DEFINITIVELY SUCCEDED OR FAILED
    var uowState = UOWStateEnum.InProcess;
    IEnumerator<string> stringIterator = connectionStringList.GetEnumerator();
    T returnVal = default(T);
    Exception lastException = null;
    string connectionString = null;
    while ((uowState == UOWStateEnum.InProcess) && stringIterator.MoveNext())
    {
        try
        {
            // TRY TO EXECUTE THE UNIT OF WORK AGAINST THE DB.
            connectionString = stringIterator.Current;
            returnVal = method(connectionString);
            uowState = UOWStateEnum.Success;
        }
        catch (Exception ex)
        {
            lastException = ex;
            // IF IT FAILED BECAUSE OF A TRANSIENT EXCEPTION,
            if (TransientChecker.IsTransient(ex))
            {
                // LOG THE EXCEPTION AND TRY AGAINST ANOTHER DB.
                Log.TransientDBException(ex, connectionString);
            }
                // ELSE
            else
            {
                // CONSIDER THE UOW FAILED.
                uowState = UOWStateEnum.Failed;
            }
        }
    }

    // LOG THE FAILURE IF WE HAVE NOT SUCCEEDED.
    if (uowState != UOWStateEnum.Success)
    {
        Log.ExceptionDuringDataAccess(lastException);
        returnVal = default(T);
    }
    return returnVal;
}

最后,对于每个操作,我们定义我们的工作单元委托方法。这是一个例子

UnitOfWorkMethod uowMethod =
    (providerConnectionString =>
     {
         using (var db = new MyContext(providerConnectionString ))
         {
             // Do my DB commands here.  They will roll back if exception thrown.
         }
     });
ExecuteUOW(uowMethod);

当调用ExecuteUOW时,它会在每个数据库上尝试委托,直到它成功或失败为止。

我将接受这个答案,因为它完全解决了原问题中提出的所有问题。但是,如果任何人提供和回答更优雅,可理解或纠正这个缺陷,我会乐意接受它。

感谢所有回复的人。