The following code persists
the DevForce local cache to a local disk file:
C#
private void StoreOffLine() {
String path = Application.UserAppDataPath + "\\MyLocalData.bin";
try {
if (!Directory.Exists(Application.UserAppDataPath)) {
Directory.CreateDirectory(Application.UserAppDataPath);
}
mPersMgr.SaveEntitySet(path);
MessageBox.Show("Changes saved to local storage");
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
VB:
Private Sub
StoreOffLine()
Dim path As String = Application.UserAppDataPath & "\MyLocalData.bin"
Try
If (Not
Directory.Exists(Application.UserAppDataPath)) Then
Directory.CreateDirectory(Application.UserAppDataPath)
End If
mPersMgr.SaveEntitySet(path)
MessageBox.Show("Changes saved to local
storage")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
The
key to the above method is the call to the PersistenceManager’s SaveEntitySet() method. When you invoke
this method, the PersistenceManager’s cache –called an “EntitySet” -- gets
stored as a binary but unencrypted file.
In the above code, the static string property Application.UserAppDataPath
returns a location similar to the following:
"C:\Documents and Settings\Mary\Application
Data\IdeaBlade\GreatAppNo638\1.0.0.0\"
but
you may of course store your entity set where you please, calling it what you
please.
Restoring the Contents of
an Entity Set
You
can load a DevForce
PersistenceManager cache from the above-created file as follows:
C#:
private void LoadOffLine()
{
String path = Application.UserAppDataPath + "\\MyLocalData.bin";
if (File.Exists(path)) {
try {
mPersMgr.RestoreEntitySet(path);
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
File.Move(path, path + "\\" + DateTime.Now.ToString());
MessageBox.Show("Local data file not found!");
}
}
}
#endregion
}
}
VB:
Private Sub
LoadOffLine()
Dim
path As String =
Application.UserAppDataPath & "\MyLocalData.bin"
If
File.Exists(path) Then
Try
mPersMgr.RestoreEntitySet(path)
LoadData()
Catch
ex As Exception
MessageBox.Show(ex.Message)
File.Move(path,
path & "\" & DateTime.Now.ToString())
MessageBox.Show("Local data file not found!")
End
Try
End If
End Sub
Assuming
you start with an empty cache, calling RestoreEntitySet()
will give you a cache in every respect like the one from which the entity set
was originally saved, including entities, query objects, checkpoints, default
save options, and default query strategy.
Encrypting the EntitySet
So far so good, but data
that’s stored unencrypted had better not be very sensitive: we all read the regular newspaper stories
about sensitive information lost or stolen when an unprotected laptop walked
away from its owner. DevForce provides
additional overloads of the SaveEntitySet() and RestoreEntitySet() methods that
permit the locally stored data to be encrypted.
You can use these as follows:
C#:
private void StoreOffLine()
{
MemoryStream aMemoryStream = new MemoryStream();
string binaryCacheDataAsString = null;
string encryptedBinaryCacheDataAsString = null;
string folderPath = System.Windows.Forms.Application.UserAppDataPath;
string filePath = null;
if (!(IsValidUserAppDataPath(folderPath))) {
MessageBox.Show("Unable to write data to folder " + folderPath + ".");
return;
} else {
filePath = folderPath + "\\DevForceCache.bin";
StreamWriter aStreamWriter = new StreamWriter(filePath, false);
try {
mPersMgr.SaveEntitySet(aMemoryStream,
true);
binaryCacheDataAsString = Convert.ToBase64String(aMemoryStream.GetBuffer());
encryptedBinaryCacheDataAsString =
IdeaBlade.Util.CryptoFns.SimpleDESEncrypt(binaryCacheDataAsString,
mEncryptionKey);
aStreamWriter.Write(encryptedBinaryCacheDataAsString);
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
finally {
aStreamWriter.Flush();
aStreamWriter.Close();
string msg = "Local cache contents saved in encrypted form to local
storage at: ";
MessageBox.Show(msg + filePath);
}
}
}
VB:
Private Sub
StoreOffLine()
Dim
streamMemory As New
MemoryStream
Dim
formatter As New
BinaryFormatter
Dim
cipherData As String
Dim
binaryData As String
Dim path As String =
Application.UserAppDataPath + "\\MyLocalData.bin"
Try
If Not Directory.Exists(Application.UserAppDataPath) Then
Directory.CreateDirectory(Application.UserAppDataPath)
End If
' 1. Open the
file
Dim fs As New
StreamWriter(path, False)
Try
' 2. Get the
PM Cache data into the Memory Stram
mPM.SaveEntitySet(streamMemory, True)
' 3. Encrypt
the binary data
binaryData =
Convert.ToBase64String(streamMemory.GetBuffer())
cipherData =
DataProtection.Encrypt(binaryData, Nothing)
' 4. Write
the data to a file
fs.Write(cipherData)
Catch ex As Exception
Dim s As String =
ex.Message
Finally
' 5. Close
the file
fs.Flush()
fs.Close()
MessageBox.Show("Local data file has been saved.")
End Try
Catch
MessageBox.Show("File
MyLocalData.bin not found!")
End Try
End Sub
In the above code, we instantiate
a MemoryStream and a StreamWriter; call SaveEntitySet() to write the cache
contents to the MemoryStream; convert the MemoryStream to an unencrypted
string; encrypt the string using any desired encryption method; and order the
StreamWriter to write that encrypted string out to the desired file.
Restoring the encrypted
entity set follows the process in reverse:
C#:
private void LoadOffLine() {
MemoryStream aMemoryStream = null;
string encryptedBinaryCacheDataAsString = null;
byte[] binaryCacheData = null;
string path = System.Windows.Forms.Application.UserAppDataPath + "\\";
try {
//Read the file
StreamReader aStreamReader = new StreamReader(path + "DevForceCache.bin");
try {
//Convert the encrypted data to a string
encryptedBinaryCacheDataAsString =
aStreamReader.ReadToEnd();
//Decrypt the encrypted string
binaryCacheData =
Convert.FromBase64String(IdeaBlade.Util.CryptoFns.SimpleDESDecrypt(
encryptedBinaryCacheDataAsString,
mEncryptionKey));
//Read the decrypted string into a MemoryStream
aMemoryStream = new MemoryStream(binaryCacheData);
//Restore the EntitySet from the decrypted
MemoryStream
mPersMgr.RestoreEntitySet(aMemoryStream, RestoreStrategy.Normal, true);
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
}
finally {
//Close the reader and reload the Employee
EntityList
aStreamReader.Close();
mEmployees = mPersMgr.GetEntities<Employee>();
this.mEmployeesBS.DataSource = mEmployees;
this.mManagersBS.DataSource = mEmployees;
}
}
catch (Exception pException) {
MessageBox.Show(pException.Message);
}
}
VB:
Private Sub
LoadOffLine()
Dim
streamMemory As MemoryStream
Dim
formatter As New
BinaryFormatter
Dim
cipherData As String
Dim
binaryData As Byte()
Dim path As String =
Application.UserAppDataPath & "\"
Try
' 1. Open the
file
Dim sr As New StreamReader(path & "MyLocalData.bin")
Try
' 2. Read the
binary data, and convert it to a string
cipherData = sr.ReadToEnd()
' 3. Decrypt
the binary data
binaryData =
Convert.FromBase64String(DataProtection.Decrypt(cipherData, Nothing))
' 4.
Rehydrate the dataset
streamMemory = New
MemoryStream(binaryData)
mPM.RestoreEntitySet(streamMemory,
RestoreStrategy.Normal, True)
Catch ex As Exception
Dim s As String =
ex.Message
Finally
' 5. Close
the reader and rebind the grid
sr.Close()
mEmployees = mPM.GetEntities(Of Employee)()
mEmpSource.DataSource = mEmployees
ConfigureGrid()
End Try
Catch
pException As Exception
MessageBox.Show(pException.Message)
MessageBox.Show("File
MyLocalData.bin not found!")
End Try
End Sub
This time, we create a
StreamReader to read the contents of the entity set file and store it into an
encrypted string; decrypt the string using a decryption method that mirrors the
one we used to encrypt the data; instantiate a memory stream, reading into it
the contents of the decrypted string containing the data; and call
RestoreEntitySet(), passing it the MemoryStream.
You can control additional
aspects of the restoration process by passing a RestoreStrategy to the
RestoreEntitySet() call.
C#:
RestoreStrategy aRestoreStrategy =
new RestoreStrategy(true, true, MergeStrategy.PreserveChanges);
mPersMgr.RestoreEntitySet(aMemoryStream,
aRestoreStrategy, true);
In the above, we’re using a
RestoreStrategy constructor whose details are as follows:

We can choose to overwrite
the DefaultSaveOptions and DefaultQueryStrategy in the target cache with those
stored in the entity set, or not; and we can determine, by specifying a
MergeStrategy, how DevForce will handle duplicates (where an entity being
restored, identified by its type and primary key value, matches an entity
already in the cache). The MergeStrategies
available are the same as those available when merging data retrieved using an
EntityQuery: NotApplicable, OverwriteChanges, PreserveChanges,
PreserveChangesUnlessOriginalObsolete, and PreserveChangesUpdateOriginal. For a
discussion of these, see the Tech Tip “Query Strategies”, available from the
IdeaBlade web site off the Tech
Tips page .
EntitySets can be used for
many different purposes, including these:
- to store data for offline work;
- to store infrequently changing reference data
locally so that it need not be retrieved from the database each
application session;
- to make incremental backups of unsaved works
You
can save as many
EntitySets as you like – just be sure you have a strategy to keep up with what
they represent!