C# 向库用户发出不一致状态警报

C# 向库用户发出不一致状态警报,c#,.net,C#,.net,在本机库上的托管包装器中,我必须完成某些操作,对于高级对象的用户来说,这些操作应该被认为是原子的和一致的。然而,本机代码中的底层操作是原子的,并且是单独一致的,而不是作为一个整体 // Simplistic look at the AddRange operation void AddRange(IEnumerable<ChildType> range) { foreach (var value in range) { this.NativeAdd(v

在本机库上的托管包装器中,我必须完成某些操作,对于高级对象的用户来说,这些操作应该被认为是原子的和一致的。然而,本机代码中的底层操作是原子的,并且是单独一致的,而不是作为一个整体

// Simplistic look at the AddRange operation
void AddRange(IEnumerable<ChildType> range)
{
    foreach (var value in range)
    {
        this.NativeAdd(value);
    }
}

// Simplistic look at the Delete operation
void Delete(ParentType value)
{
    foreach (var child in value.Children)
    {
        this.NativeDelete(child);
    }

    this.NativeDelete(value);
}
在高级AddRange和Delete例程中,我遇到了这样的情况:一些本机Add或Delete调用已经完成,但是其中一个调用中发生了错误,其余的没有完成

更新:对于用户来说,如果他们添加了7个项目,但在第7个项目中失败,或者如果他们成功添加了6个项目,那么磁盘上的实际文件看起来不会有任何不同。唯一记录错误的时间是在运行时。因此,如果用户没有意识到不一致状态的可能性,他们可能无法确定基础文件是否有效

我应该:

  • 让LibraryException通过并向用户提供ParentType或ChildType对象可能处于不一致状态的文档
  • 将LibraryException包装为不一致状态Exception,其中包含有关哪些对象可能处于不一致状态的信息
  • 捕获LibraryException并尝试“手动”回滚所做的更改,然后为用户重新引用该异常(对此不感兴趣)
  • 还有别的吗

  • 我喜欢你的第二个选择-


    这将提供一个更有意义的异常,您可以控制它。没有理由强迫您的最终用户理解一些奇怪的状态代码等。至少通过这种方式,很明显发生了什么以及原因。

    3没有真正的两阶段提交支持(即事务管理器)是不可能的。如果出现错误,手动回滚只会导致更多的不一致状态

    我认为2是多余的。如果调用父类型上的方法(这是这些方法公开的地方),并且在抛出该异常时发生异常,则假设对象上的状态已损坏(少数异常除外)。一般来说,当这种情况发生时,除了将对象扔掉并使用一个状态一致的新对象之外,您不能对该对象做太多事情


    剩下1个,来记录它。虽然您可能会在2中提供大量结构化信息,但如果对象处于不一致的状态,您确实不想尝试修复该状态,因为您会有更大的风险将其搞砸。

    如果我正确理解您的更新,调用方所能做的就是将信息传递给最终用户,谁来决定是接受国家,还是退让

    在这种情况下,(2):提供所有信息,以便调用者可以将信息、日志文件或一些统计信息传递给用户,而无需进行大量工作

    (1) 如果与(2)没有信息差异,例如知道哪个项目失败是不相关的,则可以

    (3) 如果你不能期望在大多数时候修复它,那么它是毫无意义的,如果你有一点点机会使它变得更糟,那么它就是危险的


    根据文件大小的不同,您可以通过处理副本来进行类似原子的更改。至少,用户可能希望有一种简单的方法回滚到上次保存

    调用方在遇到这种状态时应该做什么?@peterchen:我已经更新了问题,以包括失败文件对用户的外观(预览:看起来很正常)如果你指向#2,用户将无法判断他们是否去查看文件,如果7个文件中有6个成功,1个失败,或者6个成功。只有在程序执行过程中,他们才会知道程序被破坏了。这会改变你的答案吗?@sixlettvariables:不会太多。因为您在这里寻求强制ACID,如果异常通过,并且您无法回滚,那么您必须假设整个批处理是不一致的。您可以添加一些详细信息,但这样做的目的是希望用户能够(在代码中)恢复,这是一个坏主意。此外,我希望让他们知道,在某些情况下,他们不应该信任文件的一致性。不是每个错误都会导致这种情况,但在AddRange/Delete中会导致这种情况。在不知道程序死机的情况下,他们无法区分文件的好坏。我考虑过复制方案,但是,平均文件大小往往在100MB范围内。我不认为我有什么好的选择来解决这个问题。我猜它们会很大。另一个想法,但它很昂贵:作为“工作文件格式”,使用一种更改/回滚是原子的格式,并提供到原始文件格式的转换。这可以是结构化文件格式,也可以是单独的小文件。
    void NativeDelete(ChildType child)
    {
        StatusCode status = StatusCode.NoError;
        NativeMethods.DeleteChild(this.id, child.Id, out status);
    
        if (status != StatusCode.NoError)
        {
            throw new LibraryException(this, child, status);
        }
    }