Use VBA ( ADODB) in MSAccess to append data from remote DB to a local table with out locking records in the remote DB - sql

What I'm trying to do is seperate my existing MS Access application into a front-end (which will run locally on a user's machine) and backend (which will be hosted on a networked file server) and allow users to choose between "read-only" and "write" modes. The idea is that only one user can use the "write" mode at a time, thus preventing the same piece of inventory being allocated to mutliple customers. My problem is that the application currently handles concurrency by requiring users to open a .bat file which only allows them to enter application if a .ldb file does not already exist (there is no read-only mode currently), so I need to prevent users accessing the production data in "read-only" mode from creating a .ldb file and unessarily blocking out other users.
The biggest challenge to implemnting this is that users must have write access to the temporary tables in the MS Access (.mdb) file installed locally. I have tried to implement this using a linked table, but I'm not sure how I can control when records become locked using linked tables (which creates a .ldb file).

You could change the sharing setting back to Exclusive Mode. Then only one person can access the file at a time. Check out this link and the other sharing options you have.
http://office.microsoft.com/en-us/access-help/set-options-for-a-shared-access-database-mdb-HP005188297.aspx
Side note: Yikes. Using Access in a shared network environment is not fun. I hope nothing important/time sensitive/secure is in this file. The .ldb file not being deleted and blocking other users is something that I use to see happen regularly in this situation. I believe splitting the Access file into a front-end and back-end like you've done is the first step. Then using linked tables to a SQL Server database can help resolve these issues. But if you're going to this level of effort you may want to consider dumping Access and get a COTS product or create a new application.

Depending on which version of Access you are using, theres alot of flexibility in the UI developement. In other words, this sounds more like an "interface" issue as apposed to a "database" issue. Given everybody is able write to a table, you should be able to check in somewhat real time (performance can be an issue with larger datasets), whether a particular has been added to inventory or not.
They I handled this problem is have two tables, an incomming and outgoing log, and set up a query that did the math against the inventory list on the amount of products. And like a general ledger, select set amount of time to "close the log" (monthly, quarterly) so that the query is not taking into account stuff that happened two years ago.
If you need more help with Access related stuff, Access Monster is a good forum site that deal with nothing but access.

My problem is that the application currently handles concurrency by requiring users to open a .bat file which only allows them to enter application if a .ldb file does not already exist (there is no read-only mode currently), so I need to prevent users accessing the production data in "read-only" mode from creating a .ldb file and unessarily blocking out other users.
--> If every user has his own copy of the front-end on his own machine, you'd have to check the .ldb file of the back-end.
I guess it would be easier to give everyone write access to the backend and manage the actual writing programmatically with a "locked by User X" field in the backend:
You said:
preventing the same piece of inventory being allocated to mutliple customers
If this is the only reason for putting all users but one in read-only mode, you could put a "locked by User X" field on the inventory table. If someone starts to modify (or even opens) a piece of inventory, update the record with his user name, and delete the user name again when he's done.
If another user tries to open the same piece of inventory as well, the name of the first user will already be in the "locked by User X" field, so you can put the second user in read-only mode.
If the inventory pieces are not the only problem and all the other users really are not allowed to change anything as soon as someone else already is editing, you can create a new table with only one column and one row and use this as the "locked by User X" field. As soon as there is a user name inside, you can put everyone else in readonly mode.
No matter how you do it, you will have to provide some kind of admin menu, so if someone's front-end crashes while editing, someone else needs to be able to unlock this user's locked data (=delete his username from the "locked by User X" field).

Related

How to force a cache refresh in MS Access

I am working on migrating a MS Access Database over to a newer SQL platform.
But, with all of the users who are currently using it, we're migrating slowly/carefully.
The first step is that we are re-writing the VBA code into C#, which is then deployed in a .dll along with the database.
Now, the VBA code calls into the C# to do the business logic, then the VBA continues to do the displays/UI, while Access still hosts the database.
The problem comes in that I have a report that is being run after the business logic from the C# in one place, and apparently MS Access has a cache, which clears every 5 seconds. So, the transaction that occurs in the C# code writes to the database, but the VBA code is still using the cache. This is causing errors, as the records added to the database (which the VBA report is trying to report on) don't exist in the cache yet...
I'm guessing that the C# .dll must be getting treated as a "second connection" to the MS Access database, which is what seems to typically cause this error in my searches (thinks that one process is writing, and the other is reading).
Since the cache is cleared out every 5 seconds, we can just put the process to sleep, and wake it up after 5 seconds, and then run the report, but that's pretty terrible for an end user.
And, making things difficult, the cache seems like it only gets used in the deployed version (so, when running from source / in debug mode, the error never happens).
Doing some searches, there seems to be plenty of people who have said "just refresh the cache." But, the question is: within VBA, how do you refresh the cache?
Any advice would be welcome.
Thanks
I've been fighting the same issue for years as I write a lot of tools around an old Powerbuilder application that has an Access MDB back end.
The cache does exist and it is VERY real. When data is inserted on a different connection than it is queried on, the cache can be directly observed and measured. It was also documented by Microsoft before they blackholed a bunch of their old articles...
Microsoft Jet has a read-cache that is updated every PageTimeout milliseconds (default is 5000ms = 5 seconds). It also has a lazy-write mechanism that operates on a separate thread to main processing and thus writes changes to disk asynchronously. These two mechanisms help boost performance, but in certain situations that require high concurrency, they may create problems.
I've found a couple workarounds that are not the best, but somewhat make due until I find something better or can re-write the app with a better back end database.
The seemingly best answer I've found (that may actually work for you since you say you need VBA) is to use JRO.RefreshCache. I've been trying to figure out how to implement this using C# or VB.net without any luck. Below is a link to a code example where you execute the RefreshCache method on your 2nd connection that needs to pull the data. I have not tested this myself.
https://documentation.help/MSJRO/jrmthrefreshcachex.htm
A workaround I've found that will deliver the query results within 500ms to 1000ms of insert time (instead of anywhere between 500 and 5000 ms - or more):
Use System.Data.ODBC instead of OleDB, with connection string: Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=;
If someone knows how to use the JRO.RefreshCache method with OLEDB and C# or VB.net, I'd be forever grateful. I believe the issue is it's looking for an ADO connection to be passed in, not an OLEDB connection.
I not aware of ANY suggesting that some 5 second cache exits? Where did this idea come from????
Furthermore, if you have 5 users, then you not going to be able to update their cache, are you?
In other words, the issue of some cache for one user still not going to solve or work with mutli-users anyway, is it?
The simple matter is if you load up a form with 100 reocrds, and then other users are ALSO working on that 100 rows, then all users will not see other changes until such time you tell access to re-load the form.
You can do this with a me.Refresh in the form, and then it will show changes made by other users (or even your c# code!!!).
However, that not really the soluion here.
How does near EVERY system deal with this issue?
Answer:
You don't, you "design" the software to take the user work flow into account.
So, in place of loading up a form with 100 rows of data? (which you should not, unless SUPER DUPER reason exists for doing that).
The you provide a UI in which the user FIRST searches for whatever it is they want to work on.
In other words, say you just booked a user on a tour. Now, they call the office back, and want to change some details of that tour. But, a different tour staff might pick up the phone. So, now a 2nd user opens the tour?
So, you solve that issue by NOT loading all the tours into that form in the first place.
you provide a search screen, so they can search for the user, find the user, maybe type in a invoice number or whatever.
You display the results in a pick list, and then launch the form to the ONE record (and perhaps detail records from child tables).
So there no concpet of a cache in Access anymore then there is in c#.
However, if you load up a datatable in c#, and then display that data?
Well, what about the other users on that system. They will not see changes to that data ANY MORE then the current access form.
So, if you want to update some data in c#? Then fine, but you need/want to do two things:
First, before you call any c# code that may update the current form reocrd? You need to FORCE a data save of that current record BEFORE you call any code, be it VBA code, or c# code that going to update that current reocrd the user is working on.
You can in Access save the current reocrd in MANY different ways, but the typical approach is:
' single record save - current record
if me.dirty then me.dirty = false
' VBA or c# code goes here.
' optional refresh the current form to reflect changes
me.Refresh
So, in most cases, it is the "design" of your software that will solve this issue.
For example, in the tour example, or in fact ANY system, the user can't work, can't update, and can't do their job UNLESS they first find/search and have a means to bring up that form + record data in the first place.
So, ANY typical good design will:
Ask the user for that name, invoce number or whatever.
Display the results of the search, and THEN allow the user to pick the record/data to work on. When they are done, they close that form and are RIGHT BACK to the search form to do battle with the next customer or task or phone call or whatever.
So, a search form might look like this:
In above, I typed in smi, and then displayed a pick list.
The user can further type in say part of the first name, and thus now get this:
So, maybe they type in a invoice number, customer number, booking number or whatever.
So, you display the results, and then they can select the row or "thing" to work on.
thus, we click on the row (or above glasses button), and then jump to the ONE record.
so, the user does whatever they have to do with the customer. Now, when done, they close the ONE thing, the ONE main reocrd.
This not only saves the data (so others in the office can now use that booking data), but it also means the data is saved. and they are NOW right back at the search screen, ready to do battle with the next customer.
So, not only does this mean we have a VERY bandwith friednly design (we only pull the one main reocrd into that form), but it also is better for work flow.
The Access form's cache thus becomes a non issue, since we only dealing with the one record.
And as I pointed out, if the system is multi-user, then you NOT going to be able to udpate and deal with multiple users cached data anyway, are you?
Think of ANY system you EVER used from a software point of view.
When you use google, does it download the WHOLE internet, and then you use ctrl-f to search megs and megs of data in the browser?
Nope!
you search first, get a list of that search, and THEN pick one!!
And when that list is display, maybe others on the internet are udpateing, and add new data - but if that was cached in your browser, then it would not work!!!
And same goes for a desktop accounting system. You don't load up all accounts, and THEN have the user go ctrl-f to search all the data. You search for the customer, invoice number and PICK ONE to work on.
And it does not make sense to load up a form with 1000 customers, and then go ctrl-f to find that customer. Same goes for a instant banking machine. It does not download ALL customers and THEN let you search. It asks you FIRST to get what you need. So, be it browser based, desktop based, or JUST ABOUT ANY software you use?
You quite much elminate the cache issue, since not pre-loading boatloads of data, but asking and letting the user search for the data they need.
So, in regards to the Access form data and cache?
If you are on a form, and call VBA code, or c# code or whatever?
If that code update the current form, you have NO MORE OR LESS of a issue when calling VBA code, or c# code!!!! If that code updates the current form, and the reocrd is dirty (has pending edits), then you get that message about the current form's reocrd having been udpated by another user!!!
So, your cache issue does NOT IN ANY WAY exist MORE or LESS as a issue in typical Access software.
As a genreal rule, if you are on a form with pending edits, and say want to pop up some form to edit releated data?
You have to ensure that pending edits are SAVED before you launch an form that can edit the same data, or run code that can/may edit that data.
As a result, ZERO cache issues should exist, and they no more or no less exist when calling sql or VBA update code in a form then calling some c# code from that form.
So, write the pending update for that form.
Then run your VBA, SQL, or c# code.
And then do a me.Refresh to display any changes made by those external routines.
there is no documetjion, or ANY article I can find that suggests some kind of 5 seocnd cache or update - it is a urban myth, and your software challenge here in regards to use c# or VBA, or even SQL server stored procedures?
They are all the same issue, and I dare say that often access is used as a front end to SQL server, and ALL OF the SAME issues exist when using SQL server with ms-access.

Write conflict: I want to always Drop Changes

I have a split database and I have duplicated front-end file to make multiple copies for different users. Every-time a change is made on one front-end form, I want the other forms in other front-ends to always drop changes. How can I trap this write conflict to always drop changes maybe through VBA if possible?
Not quite sure what you mean by "drop changes" - the frontend should never be redesigned during normal use.
You must distribute a new copy of the frontend to the users.
A smooth and proven method using a shortcut and a script is described in my article:
Deploy and update a Microsoft Access application with one click
(If you don't have an account, browse for the link: Read the full article)
Edit:
If it is the data that is updated by several users, and you update via VBA, you may study another of my articles:
Handle concurrent update conflicts in Access silently
Though simple to use, the code is a bit too much to post here. It is also on GitHub:
VBA.ConcurrencyUpdates

Unbound checkbox in continuous form

I'm beating a dead horse here, but I still haven't found the answer I am looking for. I am throwing together an Access Database that deals with lockout procedures for our various machines at work. I have a continuous form setup so that it dynamically populates based on various complex/machine criteria. Since only portions of the machines need to be locked out at a given time, it is necessary to select the various devices from the list that was populated dynamically. When users select the various devices that they wish to lockout, they will then be able to automatically print tags for the selected devices. Which is where the unbound checkbox conundrum comes in... Yay!!!
Since it is possible for multiple users to be using the database at a given time, I don't believe that binding the checkbox to a yes/no selection within my table is the correct path to take. This is due to the fact that having multiple users picking various devices would result in additional/unnecessary tags being printed out to each user. I know that it's possible to have an unbound checkbox within a continuous form, but I have not come across any sample code that has this functionality.
If there is another path that I can take, please offer any suggestions as I am an Access novice, and am open to new ideas.
EDIT
I should mention that the database will reside within Citrix. I am not sure if this affect anything, but its worth mentioning at least.
I am assuming that you are using a client server setting, where the application file resides on a local machine. (Or on a local instance in the case of RDP / Citrix)
In that case, you can have a local table to save the checkbox information without causing any conflicts between users.
You will be using a bound checkbox, so problem solved.

How do I distribute updates to a Access database front end?

I've got an Access 2007 database that I developed which connects to SQL Server for the actual data storage. I used the Package Solution Wizard to create a distributable installer which included access runtime (with an ACCDE file) which I went around and installed on 15 or so PCs. Anyway, my question is, what is the best way to distribute updates to this database? Right now I'd need to go around and remove and reinstall. That's not a problem... I was just wondering if there was another way.
I've tried leaving the front end on a network share but it seems that most people suggest storing the front-end on the local machine, which makes sense. The problems I've run into when I leave it on a network share (at least with Access 2003 mdbs) is that I find myself needing to compact and repair often and I also have to kill the open sessions (user's who have the file open) when upgrading. I would imagine it could also hypothetically create an unnecessary bottleneck if the user was not on the local network.
Automating front-end distribution is trivial. It's a problem that has been solved repeatedly. Tony Toews's http://autofeupdater.com is one such solution that is extremely easy to implement and completely transparent to the end user.
We developed a vbscript 'launcher' for our access apps. That is what is linked to on the start menu of user's pcs and it does the following.
It checks a version.txt file located on a network share to see whether it contains different text to a locally stored copy
If the text is different it copies the access mdb and the new version.txt to the user's hard drive.
Finally it runs the mdb in access
In order to distribute an update to the user's pc all that is required is to change the text in version.txt on the network share.
Perhaps you can implement something similar to this
Make a batch file on the server (network drive).
Create a shortcut link to that batch file.
Copy the shortcut to User's Desktop.
When user double-clicks on shortcut, it will copy a fresh copy from network to local.
Replace old database.adp on the server drive when you update a new version.
Each user gets a copy of database.adp on their machine.
Remove Security warning when opening file from network share is here.
Batch File
#ECHO OFF
REM copy from network drive to local
xcopy "Your_Network_Drive\database.adp" "C:\User\database.adp" /Y /R /F
REM call your database file - Access 2007
"C:\Program Files\Microsoft Office\Office12\MSAccess.EXE" "C:\User\database.adp"
This is a very old post and I used the autofeupdater until it stopped working so I wrote one of my own and it has evolved over the last few years into something that I have used with many clients. It's so simple to use and there is no interface. Just an EXE and a very simple config file.
Please check it out here. I can also help with custom solutions if none of the configurations work for your needs. http://www.dafran.ca/MS-Access-Front-End-Loader.aspx
After trying all of the solutions above (not exactly these solutions but these are the common suggestions in the Access community), I developed a system entirely within Access using VBA that allows an admin DB to create and publish objects to client DBs without the need for user intervention or management of multiple DB files.
This approach has several benefits:
1. It simplifies the development process by having a dedicated environment (admin DB) for development and testing totally separate from the client DBs.
2. It simplifies the update/distribution process by allowing a developer to push out updates in real time that client DBs can implement in the background, without involving users. Can also allow devs to roll back to previous versions if desired.
3. It could be used as a kind of change management system within Access for developers who want to commit multiple changes to objects and modules and retain past changes.
4. It allows for easier user access control by allowing an admin to easily assign certain objects to specific users/roles without needing to maintain multiple versions of the DB.
I will hopefully post the code to GitHub soon, I just have to get clearance from my workplace to release it. I will edit this post to include the link when I have.
We have usually kept the Access front ends on network drives, and just put up with the need to compact and repair on a regular basis. You will probably find you need to do that even when they are installed locally, anyway.
If you must have it installed locally, there are various tools which will enable you to "push out" software updates, and the guys over on ServerFault would have more information on those. Assuming such tools aren't available, the only other option I can think of is to write a small loader program that checks the local .MDB against a master copy on the server, and re-copies it across if they are different, before then launching the MDB.

Best way to maintain an autocomplete/suggest list across sessions

I would like to maintain a list of all of the strings entered into a ComboBox across all uses of an application on a given PC, for use as the AutoCompleteSource for that ComboBox, i.e., I enter in "Fred" in the ComboBox, commit the data, close the application, reopen the application, reopen the ComboBox, type "F", receive the suggestion "Fred".
Assuming I already have in place code to create and maintain such a list in memory and add new elements to it as they are entered into the ComboBox, etc., what do you suggest as the best way to save/load this data in between sessions?
Possible complicating factors: This application will be deployed via OneClick deployment, and will search for updates online every time the application starts. I would prefer that if the application updates, the list still exists after the update has completed.
Thanks for your help.
Depending on your access level, storing them in a flat file seems the easiest choice.
To read the list of options, read the file and return an empty list if it isn't there.
To add an item, create the file if it isn't there and then append the option.
That means you don't send an empty file with your updates, so the data survives.
It appears that you could put the file in the Data Directory or use Isolated Storage.
I would simply use the built in User Settings (My Project > Settings > Pick "User" for the scope of the setting); they are extremely easy to retrieve and update. And although I haven't done extensive testing, I believe they remain through application updates.
Does your application use a database? If so, that would be another option for permanent storage.
Edit
If you decide to go with the User Settings option, here is a new question that addresses an issue with those settings and ClickOnce.