簡體   English   中英

正確的處理方式:對象未沿所有異常路徑放置

[英]Proper Way to Dispose: object not disposed along all exception paths

我收到第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;
    }
}

您必須確保沿任何可能的異常路徑處置所有新創建的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集合中。

首先,該shards傳遞到new ShardedDocumentStore()包含布置docStore1docStore2 這很可能會導致問題。

此外,在catch語句中,您將docStores可能已經處置的docStores

最后,當您返回時,您返回的ShardedDocumentStore正在處理(通過使用),可能使其無法用於調用者。

另外,我快速瀏覽了ShardedDocumentStore (在GitHub上),我會說它會處理它的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()的調用者處理返回的IDocumentStore

這就是為什么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塊。

考慮到CA2000:在丟失范圍文檔狀態之前處置對象 (部分來自它):

嵌套僅由一個異常處理程序保護的構造函數。 例如:

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

導致CA2000發生,因為構造StreamReader對象失敗可能導致FileStream對象永遠不會被關閉。

並且考慮到我沒有在代碼中看到提供除DocumentStore本身之外的任何一次性對象分配,我認為這是編譯器的錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM