Formulário tem minha mesa bloqueado apertado mesmo depois DoCmd.Close
-
22-07-2019 - |
Pergunta
Desculpem a parede de caras texto mas este requer expliaining, maneira muito código para postar ...
Estou a importação de arquivos de largura fixa para o acesso em métodos que exigem entrada de dados. Eu importar o arquivo usando TransferText em duas especificações (aqueles global, o outro é circunstância especial).
Eu tenho uma função que usa o DAO para percorrer todo o campo objetos em TableDefs para construir uma tabela duplicado incluindo um AutoIncrement PK, por isso tenho a capacidade de editar esses registros. Eu empurrar os dados para essa tabela com INSERT INTO.
Funciona muito bem. Os erros são encontrados, o usuário vai para a entrada de dados para corrigi-los manualmente que bate peneirar 400 linhas de caráter e reorganizar tudo da maneira seu suposto ser. Funciona muito bem!
O problema : Quando as alterações de entrada de dados são feitas a cometer botão é pressionado que chama uma função dentro de um fora módulo do formulário. Ele fecha o formulário de entrada de dados e empurra a volta informações para a tabela original menos o PK autoincremented, e é suposto para soltar a tabela replicada com ID de e gerar uma nova busca, mais uma vez por erros ...
Ele empurra de volta para o bem original, mas não vai cair tabela de ID. Sempre retorna a mim com uma mensagem indicando esta tabela está bloqueado. Ive notado a tabela é indefiniatly bloqueado até que todas as funções / saída subs. Em qualquer stepping vez através do código que não pode excluí-lo manualmente, uma vez que a execução tenha terminado eu sou capaz de removê-lo.
Estou assumindo que uma vez eu chamei isso por meio de um comando na forma, que o bloqueio não será liberado até que todos os acabamentos de código ea forma terminar pode ser chamado e fazer a sua coisa. Alguma ideia? Sim isso é muito bárbara, mas ele funciona muito bem, eu só preciso ser capaz de rasgar essa outra mesa fora do planeta para que eu possa redrop uma cópia atualizada ...
No pior caso, posso fazer o usuário fechar o formulário de fora e bater outro botão no formulário principal, mas isso está sendo projetado fortemente com compitence usuário em mente. No entanto, este tem agora toda a minha atenção e gostaria de, pelo menos, encontrar uma solução, mesmo que não é o ideal um.
-EDIT -
Duas formas são usados ??neste problema
FormA (Role: Load in and search for problems)
Examine button is pressed that:
- Uses TextTransfer based on predefined specs into tempExtract to
import the file
- DAO fires off on the Fields collection in tableDefs for
tempExtract, creates new table tempExtractID
- Performs searches through the file to find errors. Errors are saved to
a table Problem_t. Table contains Problem_ID (Set from the ID field
added to tempExtractID) and Description
- Execution of these tasks is successfully requerying the initial
form to showing a list of problems and number of occurances. A button
gains visibility, with onClick that opens the form DataEntry.
- At this point in the code after DAO execution, I can DROP the table
tempExtractID. DAO is NOT used again and was only used to build a new table.
FormB - Formulário de Entrada de Dados
Assim que eu abrir esse formulário, o tempExtractID mesa fica bloqueado e não pode cair da mesa. O OrigemDoRegistro para a forma querys tempExtractID contra o ID está em Problems_t para retornar apenas o que precisamos chave.
Eu não posso descartar a tabela até que o formulário foi totalmente encerrado. Botão no formulário de entrada de dados é pressionado para confirmar as alterações, em que não são apenas os 5 linhas de código que começa a disparar antes de eu chegar o meu erro de bloqueio.
*Xargs refers to the list of Field names pulled earlier through DAO. As DAO loops through Field objects, the physical names are added to an Xargs String which is placed in this table. Basically everything but the AutoNumber is being inserted back
docmd.Close acForm, "frmDataEntry", acSaveNo
call reInitializeExtract
> docmd.RunSQL "DELETE FROM tempExtract"
> docmd.RunSQL "INSERT INTO tempExtract SELECT (" & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
docmd.DeleteObject acTable, "tempExtractID"
Este é o único código que é executado entre o momento em que o formulário é aberto (onde a tabela primeira fica bloqueado) e continua a ser bloqueada até que todos os subs & funções tenham concluído.
Solução
Eu sugiro definir o OrigemDoRegistro do formulário para vbNullString e excluindo a mesa. Isso deve funcionar, a menos que você também tem comboboxes e assim por diante obrigado a esta tabela.
Outras dicas
Sem código é difícil dizer, mas se você estiver usando DAO, você precisa limpar seus objetos de código. Isso significa definir como Nothing seus objetos de banco de dados, e fechar e definir como Nothing quaisquer objetos de registros.
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = DBEngine.OpenDatabase("[path to database]")
Set rs = db.OpenRecordset("[SELECT statement]")
rs.Close
Set rs = Nothing
db.Execute("[DML or DDL statement]", dbFailOnError)
db.Close
Set db = Nothing
Set db =CurrentDB
Set rs = db.OpenRecordset("[SELECT statement]")
rs.Close
Set rs = Nothing
Set db = Nothing ' you don't close a db variable initialized with CurrentDB
Enquanto VBA é suposto para limpar estes objetos quando eles saem de escopo, não é 100% confiável (porque VBA utiliza a contagem de referência para acompanhar se um objeto pode ser lançado, e isso nem sempre sabe quando todos as referências foram apagadas).
Objetos deixados em aberto é a fonte mais provável dos bloqueios, então você deve certificar-se de que você está limpando suas variáveis ??de objeto depois que terminar com eles.
EDIT depois de ver que você está usando DoCmd.RunSQL:
Usando DoCmd.RunSQL é provável que a causa do problema. É certamente algo que tira a sua gestão programática de suas conexões. Se você usar DAO em vez disso, você terá controle sobre a conexão, bem como evitar a armadilha real da DoCmd.RunSQL, o que é que ele não lida com erros. Se uma instrução DML ou DDL não pode concluir com êxito na íntegra, a coisa toda deve falhar. Por exemplo, se você está anexando 100 registros e 10 deles falhar por violações de chave, DoCmd.RunSQL, transparentemente, anexar os 90 e não informarem o 10 falhas. É o mesmo com atualizações e qualquer outra declaração DML / DDL. DoCmd.RunSQL "amavelmente" completa em silêncio, enquanto muitas das atualizações como ele pode, deixando-o sem ter idéia do que alguns dos que não conseguiu completar.
Com certeza, em alguns casos, você pode querer que isso aconteça, por exemplo, se você está anexando registros que você sabe pode ter colisões PK e não quer gastar os ciclos de CPU em uma associação externa que elimina as duplicatas da conjunto de registros que você está anexando.
Mas a maior parte do tempo, que não é o caso.
Como eu disse no meu comentário acima, eu uso uma função que é projetado para substituir transparente DoCmd.RunSQL e utiliza um DAO Executar declaração e tratamento de erros. Tenho postado um par de vezes no SO ( aqui está um ), e aqui está a versão que tenho em uso em produção em meu projeto de desenvolvimento atualmente mais ativo:
Public Function SQLRun(strSQL As String, Optional db As Database, _
Optional lngRecordsAffected As Long) As Long
On Error GoTo errHandler
Dim bolCleanup As Boolean
If db Is Nothing Then
Set db = CurrentDb
bolCleanup = True
End If
'DBEngine.Workspaces(0).BeginTrans
db.Execute strSQL, dbFailOnError
lngRecordsAffected = db.RecordsAffected
'DBEngine.Workspaces(0).CommitTrans
exitRoutine:
If bolCleanup Then
Set db = Nothing
End If
SQLRun = lngRecordsAffected
'Debug.Print strSQL
Exit Function
errHandler:
MsgBox "There was an error executing your SQL string: " _
& vbCrLf & vbCrLf & Err.Number & ": " & Err.Description, _
vbExclamation, "Error in SQLRun()"
Debug.Print "SQL Error: " & strSQL
'DBEngine.Workspaces(0).Rollback
Resume exitRoutine
End Function
(as transações são comentados porque eles estavam causando problemas que não têm tempo para solucionar problemas)
Você pode substituir essas linhas de sua:
DoCmd.RunSQL "DELETE FROM tempExtract"
DoCmd.RunSQL "INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
... com isto:
SQLRun "DELETE FROM tempExtract"
SQLRun "INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
Você também pode fazer isso:
Debug.Print SQLRun("DELETE FROM tempExtract") & " records deleted."
Debug.Print SQLRun("INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" _
& ") FROM tempExtractID") & " records inserted."
Uma vez que a função retorna o .RecordsAffected para cada executar, você pode imprimir para a janela imediata, ou você pode atribuir o valor de retorno a uma variável, ou passar uma variável existente através a ele e trabalhar com essa variável assim:
Dim lngRecordsAffected As Long
...
Call SQLRun("DELETE FROM tempExtract", , lngRecordsAffected)
Debug.Print lngRecordsAffected & " records deleted."
Call SQLRun("INSERT INTO tempExtract SELECT (" _
& DLookup("Value", "CONFIG_t", "Item = 'Xargs'" _
& ") FROM tempExtractID", , lngRecordsAffected)
Debug.Print lngRecordsAffected & " records inserted."
O ponto é que se houver erros na instrução de execução, a coisa toda vai falhar (e aparecer uma mensagem de erro - você pode querer mudá-lo de modo que se houver um erro ele retorna -1 ou alguma vez de popping um MsgBox).
Eu uso essa função na maioria das vezes, passando em uma variável de banco de dados pré-armazenados em cache, então eu não quero limpá-lo depois. Se você estiver usando um banco de dados diferente que não seja CurrentDB (), você realmente quer ter certeza de qualquer variável de banco de dados apontando para o seu db externa está fechada e definida como Nothing. Sem isso, os bloqueios são mantidos nos objetos de banco de dados de nível superior e o arquivo LDB permanece aberta e ativa.