Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.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#_Design Patterns - Fatal编程技术网

C# 如何确保系统级操作是原子级的?有图案吗?

C# 如何确保系统级操作是原子级的?有图案吗?,c#,design-patterns,C#,Design Patterns,我有一个方法,在内部按顺序执行不同的子操作,在任何子操作失败时,我希望回滚整个操作 我的问题是子操作并不是所有的数据库操作。这些主要是系统级更改,如在windows注册表中添加内容、在指定路径上创建文件夹和设置权限等。子操作可能不止这些 想做这样的事 CreateUser(){ CreateUserFtpAccount(); CreateUserFolder(); SetUserPermission(); CreateVirtualDirector

我有一个方法,在内部按顺序执行不同的子操作,在任何子操作失败时,我希望回滚整个操作

我的问题是子操作并不是所有的数据库操作。这些主要是系统级更改,如在windows注册表中添加内容、在指定路径上创建文件夹和设置权限等。子操作可能不止这些

想做这样的事

CreateUser(){

     CreateUserFtpAccount();

     CreateUserFolder();

     SetUserPermission();

     CreateVirtualDirectoryForUser();


     ....
     ....
     ....
     and many more

}
如果最后一个操作失败,我想回滚以前的所有操作

那么,标准的方法是什么呢?有没有一种设计模式可以处理这个问题


注意:我使用的是
C#.net

我不知道这种类型的东西有任何标准模式,但我自己可能会使用嵌套的try/catch块来实现这一点——在catch块中使用适当的代码回滚非数据库操作。使用TransactionScope确保所有数据库操作都是TransactionAction的

例如:


这里有一种方法:

使用,可以创建可撤消的操作。在每次操作中,您都会注册相关命令,以便在出现故障情况时可以撤消执行的命令

例如,这可能都属于类似事务的上下文对象,该对象实现了
IDisposable
,并使用
块放入
。可撤消的操作将注册到此上下文对象。在dispose上,如果未提交,则对所有已注册的命令执行“撤消”。希望能有帮助。缺点是您可能需要将一些方法转换为类。不过,这可能是一种必要的邪恶

代码示例:

using(var txn = new MyTransaction()) {
  txn.RegisterCommand(new CreateUserFtpAccountCommand());
  txn.RegisterCommand(new CreateUserFolderCommand());
  txn.RegisterCommand(new SetUserPermissionCommand());
  txn.RegisterCommand(new CreateVirtualDirectoryForUserCommand());
  txn.Commit();
}

class MyTransaction : IDisposable {
  public void RegisterCommand(Command command){ /**/ }
  public void Commit(){ /* Runs all registered commands */ }
  public void Dispose(){ /* Executes undo for all registered commands */ }
}

class UndoableCommand {
  public Command(Action action) { /**/ }
  public void Execute() { /**/ }
  public void Undo{ /**/ }
}
更新:

你提到你有数百个这样的可逆操作。在这种情况下,您可以采取更实用的方法,完全摆脱
UndoableCommand
。您将改为注册代理,如下所示:

using(var txn = new MyTransaction()) {
  txn.Register(() => ftpManager.CreateUserAccount(user),
               () => ftpManager.DeleteUserAccount(user));
  txn.Register(() => ftpManager.CreateUserFolder(user, folder),
               () => ftpManager.DeleteUserFolder(user, folder));
  /* ... */
  txn.Commit();
}

class MyTransaction : IDisposable {
  public void Register(Action operation, Action undoOperation){ /**/ }
  public void Commit(){ /* Runs all registered operations */ }
  public void Dispose(){ /* Executes undo for all registered and attempted operations */ }
}

需要注意的是,您需要小心使用这种方法。

我认为最好的办法是封装流程中每个步骤的执行和反转。它将使代码比嵌套的try-catch块更易于读取。比如:

public interface IReversableStep
{
    void DoWork();
    void ReverseWork();
}

public void DoEverything()
{
var steps=新列表()
{
新建CreateUserFTPAccount(),
新建CreateUserFolder(),
...
}
var completed=新列表();
尝试
{
foreach(var步进)
{
步骤.道尔克();
已完成。添加(步骤);
}
}
捕获(例外)
{
//如果必须先撤消最近的操作,
//只需颠倒列表:
完成。反向();
已完成的.ForEach(x=>x.ReverseWork());
}
}

NTFS和注册表都支持在KTM和MS DTC事务中注册(扩展为
TransactionScope
)。但是,由于复杂性,事务性文件系统已被弃用,并且可能不在windows的某些未来版本中


如果不是每件事都适合一个事务,我当然会参考这个问题的其他答案中提供的命令历史模式。

您查找了transactionscope吗?->请记住
反向作业
中可能出现的异常。如果发生这种情况,将只执行部分回滚。此外,您可能希望按相反的顺序执行反转步骤,而不是按命令执行的顺序。但我确实喜欢这个概念!我有数百个这样的可逆命令(diff类中的方法),所以不能将每个方法都作为单独的类。我想,我需要简单地使用if-else/尝试捕捉逻辑。还有其他想法吗?很好,我会在Register方法中使用Func delegate而不是Action,因为我的方法总是接受并返回一个对象(DTO),这是Action不允许的。或者是吗?你是对的,动作委托是无效的,不允许有返回值。您可以自由使用Func,因为MyTransaction.Commit和.Dispose的实现将掌握在您的手中。是的,这是我的最后一个选项,我正试图找到一种避免的方法。
public interface IReversableStep
{
    void DoWork();
    void ReverseWork();
}
public void DoEverything()
{
    var steps = new List<IReversableStep>()
    {
         new CreateUserFTPAccount(),
         new CreateUserFolder(),
         ...
    }
    var completed = new List<IReversableStep>();
    try 
    {
         foreach (var step in steps)
         {
              step.DoWork();
              completed.Add(step);
         }
    }
    catch (Exception)
    {
         //if it is necessary to undo the most recent actions first, 
         //just reverse the list:
         completed.Reverse(); 
         completed.ForEach(x => x.ReverseWork());
    }
}