正确的处理方式:对象未沿所有异常路径放置

时间:2012-04-12 13:58:21

标签: c# dispose idisposable ravendb ca2000

我收到第84行和第85行的消息(两个,堆叠使用行):

  

CA2000:Microsoft.Reliability:在方法'RavenDataAccess.GetRavenDatabase()'中,对象'<> g_ initLocal9'未沿所有异常路径放置。在对对象'<> g _initLocal9'调用System.IDisposable.Dispose之前,对它的所有引用都超出范围。

DocumentStore实现了IDisposable。

为什么呢?我还能如何处置DocumentStore对象?它们是在一个使用块中创建的,我将它们放在我的catch块中。该如何解决?

private static IDocumentStore GetRavenDatabase()
{
    Shards shards = new Shards();

    try
    {
        using (DocumentStore docStore1 = new DocumentStore { Url = ConfigurationManager.AppSettings["RavenShard1"] })  // Line 84
        using (DocumentStore docStore2 = new DocumentStore { Url = ConfigurationManager.AppSettings["RavenShard2"] })  // Line 85
        {
            shards.Add(docStore1);
            shards.Add(docStore2);
        }

        using (ShardedDocumentStore documentStore = new ShardedDocumentStore(new ShardStrategy(), shards))
        {
            documentStore.Initialize();

            IndexCreation.CreateIndexes(typeof(RavenDataAccess).Assembly, documentStore);

            return documentStore;
        }
    }
    catch
    {
        shards.ForEach(docStore => docStore.Dispose());

        throw;
    }
}

4 个答案:

答案 0 :(得分:2)

您必须确保沿着任何可能的异常路径处置所有新创建的Disposable对象。见下文:

private static IDocumentStore GetRavenDatabase()
{
    Shards shards = new Shards();
    DocumentStore docStore1 = null;
    DocumentStore docStore2 = null;

    ShardedDocumentStore shardedDocumentStore = null;
    ShardedDocumentStore tempShardedDocumentStore = null;

    try
    {
        docStore1 = new DocumentStore();
        docStore1.Url = ConfigurationManager.AppSettings["RavenShard1"];
        docStore2 = new DocumentStore();
        docStore2.Url = ConfigurationManager.AppSettings["RavenShard2"];

        shards.Add(docStore1);
        shards.Add(docStore2);

        tempShardedDocumentStore = new ShardedDocumentStore(new ShardStrategy(), shards);
        tempShardedDocumentStore.Initialize();

        IndexCreation.CreateIndexes(typeof(RavenDataAccess).Assembly, tempShardedDocumentStore);

        docStore1 = null;
        docStore2 = null;

        shardedDocumentStore = tempShardedDocumentStore;
        tempShardedDocumentStore = null;

        return shardedDocumentStore;
    }
    finally
    {
        if (tempShardedDocumentStore != null) { tempShardedDocumentStore.Dispose(); }
        if (docStore1 != null) { docStore1.Dispose(); }
        if (docStore2 != null) { docStore2.Dispose(); }
    }
}

CA似乎有内联属性初始值设定项的问题,但是如果你将它们分解出来,这应该可行。关键是要确保无论try块中抛出异常的位置,都可以清除所有可以处置的新对象。

通过在返回之前设置临时引用,您不再需要nulldocStore1docStore2tempShardedDocumentStore),您可以检查finally块看看它们实际上是否设置为null,如果没有,则在某处发生异常,您可以在执行离开此方法之前处置它们。

注意 docStore1docStore2是临时引用,因为它们已添加到Shards集合中。

答案 1 :(得分:1)

首先,您传入shards的{​​{1}}包含已处置的new ShardedDocumentStore()docStore1。这很可能会导致问题。

此外,在catch语句中,您将处置可能已经处理的docStore2

最后,当您返回时,您返回的docStores正在处理(通过使用),可能会使调用者无法使用它。

另外,我快速浏览了ShardedDocumentStore(在GitHub上),我会说它负责处理ShardedDocumentStore。也就是说,你不应该处理它。

将您的代码更改为:

docStores

...让private static IDocumentStore GetRavenDatabase() { ShardedDocumentStore documentStore = null; var docStore1 = null; var docStore2 = null; try { Shards shards = new Shards(); docStore1 = new DocumentStore { Url = ConfigurationManager.AppSettings["RavenShard1"] }; shards.Add(docStore1); docStore2 = new DocumentStore { Url = ConfigurationManager.AppSettings["RavenShard2"] }; shards.Add(docStore2); documentStore = new ShardedDocumentStore(new ShardStrategy(), shards); documentStore.Initialize(); IndexCreation.CreateIndexes(typeof(RavenDataAccess).Assembly, documentStore); return documentStore; } catch { if (documentStore != null) { documentStore.Dispose(); } else { if (docStore2 != null) docStore2.Dispose(); if (docStore1 != null) docStore1.Dispose(); } throw; } } 的来电者处理退回的GetRavenDatabase()

答案 2 :(得分:1)

这就是为什么using语句中的对象初始值设定项会导致CA警告:

您的代码如下所示:

using (DocumentStore docStore2 = new DocumentStore { Url = ConfigurationManager.AppSettings["RavenShard2"] })  // Line 85
{
   ...
}
由于对象初始化器的工作方式,

... essentialy成为了这个:

DocumentStore foo = new DocumentStore;
foo.Url = ConfigurationManager.AppSettings["RavenShard2"];
using(DocumentStore docStore2 = foo)
{
   ...
}

正如您所看到的,DocumentStore的初始化现在发生在using {}块之外,因此如果设置temp.Url的行引发异常,则不会丢弃DocumentStore。

有许多变通方法,例如将参数传递给对象的构造函数,在using语句中设置属性而不是使用对象初始化器,或者使用try / finally块。

答案 3 :(得分:0)

考虑到CA2000: Dispose objects before losing scope文档状态(部分来自它):

  

嵌套仅受一个异常处理程序保护的构造函数。例如:

     

使用(StreamReader sr = new StreamReader(new    FileStream (“C:\ myfile.txt”,FileMode.Create))){...}

     

导致CA2000发生,因为构造失败了   StreamReader对象永远不会导致 FileStream 对象   被关闭。

考虑到我在代码中没有看到提供了除DocumentStore之外的任何一次性对象分配,我认为这是编译器的错误。