laccdbファイルが消えずに残ります

タグの編集
投稿者 還暦  (社会人) 投稿日時 2020/3/7 12:50:37
初めて書き込ませていただきます。VB中学校様にはいつも大変お世話になっています。
VB+ACCESSでプログラムを作成していますが、どうしても解決できない問題が発生したため相談させてください。

環境:Windows10
        Visual Studio 2013
        Access DataBaseEngin 2010

問題点:VB.Netでaccdbファイルを開いて処理を行うとlaccdbファイルが消えずに残る

以前は問題なく動作していたプログラムが動かなくなり困っています。
データベースコネクションをDisposeすればlaccdbファイルは消えていたのですが、
最近になって消えずに残るためにエラーが起こります。プログラム上から削除することもできません。
おそらくWindows10にしてからだと思います。具体的作業は、

1.大量の個人用DBに新しいTableを追加する
  ファイル名のリストを配列に入れ、For~NextループでDBを処理しています。
  laccdbが消えないため、64個でエラーになります。

2.作業用DBを読み込んだ後に削除する
  読み込んだ後にlaccdbが残るために削除できません。

コネクションプーリングを無効にするとかUsingでDBを開くとか、google先生に教えてもらった方法を試して
みましたが上手くいきません。どなたか解決方法をご存じないでしょうか。よろしくお願いいたします。
投稿者 るきお  (社会人) 投稿日時 2020/3/7 13:09:43
回答ではありません。

一般的には、きちんと終了処理が行われていないのでlaccdbファイルがファイルが残るのだと思いますが、おそらく、還暦さんの事例において、ずばり、どうすればその問題を解決できるか指摘できる人はいないと思います。

その理由は、
第一に、きちんと終了処理を行うには、さまざまな要因を考慮する必要があり、どうすればよいかを掲示板で説明するにはかなり複雑であること。
第二に、そもそも「どうすればきちんと終了されるか」という条件を明確に理解している人はまずいないであろうということ。
第三に、還暦さんがどのようにAccessにアクセスして、どのような処理(たとえば、ExecuteScalar?それともAdapter.Fill?)をして、どのように終了しているかは不明であることです。

こういった場合のアプローチとしては、
現象が発生する最小限のコードを作成して、それを投稿して相談するのが一般的です。
そうすれば多くの人が、還暦さんのコードを試すことができるので、何か指摘できる人が増えます。
それに「Accessをきちんと終了する方法」を完全には理解していなくても、還暦さんの場合はこうすればいいんじゃないかのような話をすることもできるようになります。

現象が発生する最小限のコードを作成することは、問題の解決に直接つながることも多々あります。
つまり、あと1行(1語)でも何かを削ったら現象が発生しなくなる状態ということですから、その1行(1語)に問題があるのではないかと何か気付く場合が多いからです。(そう単純でないこともしばしばですが)

ただ、残念ながら私はもう10年以上Accessを扱ったことがないため、そのようなアプローチをされてもおそらく私はお力になれないと思いますが、このような理由で現象が発生する最小限のコードを作成されてみることをお勧めします。

もちろん、この掲示板をご覧の方で、経験上なにかアドバイスしてくれる方もいるかもしれません。
投稿者 還暦  (社会人) 投稿日時 2020/3/7 14:04:17
るきお様

アドバイス有り難うございます。
作成したプログラムはシンプルなもので、以下のようなものです。
Form1にButton1を配置しています

***** 以下コード

Imports System.Data.OleDb

Public Class Form1
    '個人データファイルの一覧を入れる配列
    Dim PersonalFileList() As String

    '初期化
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        '個人データファイル一覧を収得
        PersonalFileList = System.IO.Directory.GetFiles("作業用フォルダ", "*.accdb")
    End Sub

    'Start
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        '追加するテーブルの設定
        Dim NewTable As String = "CREATE TABLE NewTable(ID INT, NAME STRING・・・・)
       
        '全個人データファイルにNewTableを追加する
        For i As Integer = 0 To PersonalFileList.Count - 1
            Try
                Dim Cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & PersonalFileList(i))
                Dim SQLCm As OleDbCommand = Cn.CreateCommand
                Cn.Open()
                SQLCm = New OleDbCommand(NewTable, Cn)
                SQLCm.ExecuteNonQuery()
                Cn.Close()
                SQLCm.Dispose()
                Cn.Dispose()
            Catch ex As Exception
                MsgBox(Err.Description)
            End Try
        Next i
        MsgBox("終了しました")
    End Sub

End Class

***** ここまでコード

4年前には問題なく動いたプログラムです。Cn.Dispose()してもlaccdbファイルが消えないためエラーになります。
投稿者 るきお  (社会人) 投稿日時 2020/3/7 14:56:28
問題解決につながるかはわかりませんが、修正すべき点がいくつかあるのはわかりました。
現在のプログラムをできるだけ変更しないで書き換えると次のようになります。
'追加するテーブルの設定 
Dim NewTable As String = "CREATE TABLE NewTable(ID INT, NAME STRING・・・・)"

'全個人データファイルにNewTableを追加する 
For i As Integer = 0 To PersonalFileList.Count - 1
    Try
        Using Cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & PersonalFileList(i))
            Using SQLCm As OleDbCommand = Cn.CreateCommand
                Cn.Open()
                SQLCm.CommandText = NewTable
                SQLCm.ExecuteNonQuery()
                Cn.Close()
            End Using
        End Using

    Catch ex As Exception
        MsgBox(Err.Description)
    End Try
Next i
MsgBox("終了しました")


ポイント1
SQLCm を Cn.CreateCommand で作成した後、もう1回 New OleDbCommand で作成しています。
SQLCm.Dispose() はその後で呼ばれるので、DisposeされるのはNewで作成された方のOleDbCommandとなり、CreateCommandされた方のSQLCmは残ります。
※残った場合、.NET Frameworkが頃合いを見計らって自動的に開放してくれますが、この「頃合い」は自動制御なのですぐかもしれませんし、1時間後かもしれません。

ポイント2
DispoeをUsing ~ End Usingに置き換えました。
Using ~ End Usingを使うと必ず解放されるので、VB2005以降ではこの書き方が推奨されています。
還暦さんのプログラムの場合、例外が発生するとDisposeされずにCatchに流れる可能性がありますが、Using ~ End Usingで囲んで小谷、Catchに流れた場合でも、(他のどこか別の場所にジャンプした場合でも)ブロックから抜けるときに必ずDisposeが呼ばれます。
投稿者 還暦  (社会人) 投稿日時 2020/3/7 16:33:35
るきお様

ありがとうございます。
職場で作業していますので、明日にでも確認させていただきます。
結果はその後にご報告いたします。
投稿者 還暦  (社会人) 投稿日時 2020/3/8 13:16:33
るきお様

先ほど試させていただきました。
結果、上手くいきませんでした。
MsgBoxをコメントアウトしてエラーを無視して続行させたところ、
約80秒後にすべてのlaccdbファイルが消えましたが、その後は別のエラーが起こっていました。
またWindows7で同じ作業をしてみたところ、laccdbファイルが残ることはなく、問題なく終了しました。
また問題点2につきましても、やはりWindows7ではlaccdbファイルが残ることなく実行可能でした。
やはりWindows10の問題のようですが、今さらサポートが終了した7に戻すこともできません。
プログラム上から強制的にlaccdbファイルを消す(ロック解除?)方法はないものでしょうか?
投稿者 るきお  (社会人) 投稿日時 2020/3/8 14:33:02
laccdbファイルが消えずに残ることが問題だったのですよね?
そして、プログラムを変更した結果、laccdbファイルが消えるようになったのですよね。

今は何に困っているのでしょうか?

発生しているエラーが「問題」なのであれば、エラーメッセージやなにか情報を書き込まれると、アドバイスしてくれる方がいるかもしれませんよ。

>プログラム上から強制的にlaccdbファイルを消す(ロック解除?)方法はないものでしょうか?
一般的にはかっこで書かれていますようにロックを解除する方法を探すべきで、強制的にファイルを削除すると別の問題が発生するかもしれませんね。
投稿者 還暦  (社会人) 投稿日時 2020/3/8 15:06:49
るきお様

プログラムを変更した結果、laccdbが消えたわけではなく、Windows7の環境ではエラーが起きなかっただけです。
Windows10では上手く動かないままでした。

いろいろ試した結果、コネクションプーリングを無効にすることができました。
接続時に「OLE DB Services=-4;」を追加し、
Using Cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;OLE DB Services=-4;Data Source=" & PersonalFileList(i))
とするとlaccdbファイルがすぐに消えるようになりました。
コネクションプーリングを無効にすることが良いか悪いかは分かりませんが、とりあえずエラーは回避できました。

相談に乗っていただき、誠にありがとうございました。
これからもよろしくお願いいたします。