//******************************************************************************************************
// IsamDataFileBase.cs - Gbtc
//
// Copyright © 2012, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://www.opensource.org/licenses/eclipse-1.0.php
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 03/08/2007 - Pinal C. Patel
// Original version of source code generated.
// 11/30/2007 - Pinal C. Patel
// Modified the "design time" check in EndInit() method to use LicenseManager.UsageMode property
// instead of DesignMode property as the former is more accurate than the latter.
// 09/19/2008 - J. Ritchie Carroll
// Converted to C#.
// 10/28/2008 - Pinal C. Patel
// Edited code comments.
// 12/04/2008 - J. Ritchie Carroll
// Modified class an example to use new ISupportBinaryImage.
// 05/12/2009 - Pinal C. Patel
// Optimized Read() for better memory management by using "yield return".
// 05/19/2009 - Pinal C. Patel
// Implemented the IProvideStatus interface.
// 07/02/2009 - Pinal C. Patel
// Modified state alterning properties to reopen the file when changed.
// 08/10/2009 - Pinal C. Patel
// Modified Write() to write empty intermediate records that are missing to avoid garbage data
// for the missing intermediate records when records are being written to disk directly.
// 09/14/2009 - Stephen C. Wills
// Added new header and license agreement.
// 09/16/2009 - Pinal C. Patel
// Modified Save() to flush buffered data to disk.
// Removed MinimumRecordCount property - not very useful.
// 09/17/2009 - Pinal C. Patel
// Added exception handling to event handlers.
// 12/04/2009 - Pinal C. Patel
// Fixed thread synchronization bug in ReadFromDisk().
// 04/07/2011 - Pinal C. Patel
// Removed inheritance from Component class to allow for serialization.
// 11/23/2011 - J. Ritchie Carroll
// Modified to support buffer optimized ISupportBinaryImage.
// 12/06/2011 - Pinal C. Patel
// Updated to instantiate a FileSystemWatcher object that watches for changes to the file content
// only if needed to avoid a bug introduced in .NET 4.0 that causes memory leak.
// 12/14/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Text;
using System.Threading;
using System.Timers;
using System.Xml.Serialization;
using GSF.Configuration;
using GSF.Parsing;
using Timer = System.Timers.Timer;
namespace GSF.IO
{
/// <summary>
/// An abstract class that defines the read/write capabilities for ISAM (Indexed Sequential Access Method) file.
/// </summary>
/// <typeparam name="T">
/// <see cref="Type"/> of the records the file contains. This <see cref="Type"/> must implement the <see cref="ISupportBinaryImage"/> interface.
/// </typeparam>
/// <remarks>
/// <para>
/// This ISAM implementation keeps all the records in memory, so it may not be suitable for very large files. Since data is stored
/// in memory using a list, the maximum number of possible supported records will be 2,147,483,647 (i.e., Int32.MaxValue).
/// </para>
/// <para>
/// See <a href="http://en.wikipedia.org/wiki/ISAM" target="_blank">http://en.wikipedia.org/wiki/ISAM</a> for more information on ISAM files.
/// </para>
/// </remarks>
/// <example>
/// This example shows a sample implementation of <see cref="IsamDataFileBase{T}"/>:
/// <code>
/// using System;
/// using System.Text;
/// using GSF;
/// using GSF.IO;
/// using GSF.Parsing;
///
/// class Program
/// {
/// static void Main(string[] args)
/// {
/// // Create a few test records.
/// TestIsamFileRecord r1 = new TestIsamFileRecord(1);
/// r1.Name = "TestRecord1";
/// r1.Value = double.MinValue;
/// r1.Description = "Test record with minimum double value";
/// TestIsamFileRecord r2 = new TestIsamFileRecord(2);
/// r2.Name = "TestRecord2";
/// r2.Value = double.MaxValue;
/// r2.Description = "Test record with maximum double value";
///
/// // Open ISAM file.
/// TestIsamFile testFile = new TestIsamFile();
/// testFile.FileName = "TestIsamFile.dat";
/// testFile.Open();
///
/// // Write test records.
/// testFile.Write(r1.Index, r1);
/// testFile.Write(r2.Index, r2);
///
/// // Read test records.
/// Console.WriteLine(testFile.Read(1));
/// Console.WriteLine(testFile.Read(2));
///
/// // Close ISAM file.
/// testFile.Close();
///
/// Console.ReadLine();
/// }
/// }
///
/// class TestIsamFile : IsamDataFileBase<TestIsamFileRecord>
/// {
/// /// <summary>
/// /// Size of a single file record.
/// /// </summary>
/// protected override int GetRecordSize()
/// {
/// return TestIsamFileRecord.RecordLength;
/// }
///
/// /// <summary>
/// /// Creates a new empty file record.
/// /// </summary>
/// protected override TestIsamFileRecord CreateNewRecord(int id)
/// {
/// return new TestIsamFileRecord(id);
/// }
/// }
///
/// class TestIsamFileRecord : ISupportBinaryImage
/// {
/// private int m_index;
/// private string m_name; // 20 * 1 = 20
/// private double m_value; // 1 * 8 = 8
/// private string m_description; // 100 * 1 = 100
///
/// public const int RecordLength = 128; // Total = 128
///
/// public TestIsamFileRecord(int recordIndex)
/// {
/// m_index = recordIndex;
///
/// Name = string.Empty;
/// Value = double.NaN;
/// Description = string.Empty;
/// }
///
/// /// <summary>
/// /// 1-based index of the record.
/// /// </summary>
/// public int Index
/// {
/// get { return m_index; }
/// }
///
/// /// <summary>
/// /// Name of the record.
/// /// </summary>
/// public string Name
/// {
/// get { return m_name; }
/// set { m_name = value.TruncateRight(20).PadRight(20); }
/// }
///
/// /// <summary>
/// /// Value of the record.
/// /// </summary>
/// public double Value
/// {
/// get { return m_value; }
/// set { m_value = value; }
/// }
///
/// /// <summary>
/// /// Description of the record.
/// /// </summary>
/// public string Description
/// {
/// get { return m_description; }
/// set { m_description = value.TruncateRight(100).PadRight(100); }
/// }
///
/// /// <summary>
/// /// Serialized record length.
/// /// </summary>
/// public int BinaryLength
/// {
/// get { return RecordLength; }
/// }
///
/// /// <summary>
/// /// Serialized record data.
/// /// </summary>
/// public byte[] BinaryImage
/// {
/// get
/// {
/// // Serialize TestIsamFileRecord into byte array.
/// byte[] image = new byte[RecordLength];
/// Buffer.BlockCopy(Encoding.ASCII.GetBytes(Name), 0, image, 0, 20);
/// Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, image, 20, 8);
/// Buffer.BlockCopy(Encoding.ASCII.GetBytes(Description), 0, image, 28, 100);
///
/// return image;
/// }
/// }
///
/// /// <summary>
/// /// Deserializes the record.
/// /// </summary>
/// public int Initialize(byte[] binaryImage, int startIndex, int length)
/// {
/// if (length >= RecordLength)
/// {
/// // Deserialize byte array into TestIsamFileRecord.
/// Name = Encoding.ASCII.GetString(binaryImage, startIndex, 20);
/// Value = BitConverter.ToDouble(binaryImage, startIndex + 20);
/// Description = Encoding.ASCII.GetString(binaryImage, startIndex + 28, 100);
/// }
/// else
/// throw new InvalidOperationException("Invalid record size, not enough data to deserialize record");
///
/// return RecordLength;
/// }
///
/// /// <summary>
/// /// String representation of the record.
/// /// </summary>
/// public override string ToString()
/// {
/// return string.Format("Name: {0}, Value: {1}, Description: {2}", Name, Value, Description);
/// }
/// }
/// </code>
/// </example>
public abstract class IsamDataFileBase<T> : ISupportLifecycle, IProvideStatus, IPersistSettings where T : ISupportBinaryImage
{
#region [ Members ]
// Constants
/// <summary>
/// Specifies the default value for the <see cref="FileName"/> property.
/// </summary>
public const string DefaultFileName = "IsamDataFile.dat";
/// <summary>
/// Specifies the default value for the <see cref="FileAccessMode"/> property.
/// </summary>
public const FileAccess DefaultFileAccessMode = FileAccess.ReadWrite;
/// <summary>
/// Specifies the default value for the <see cref="AutoSaveInterval"/> property.
/// </summary>
public const int DefaultAutoSaveInterval = -1;
/// <summary>
/// Specifies the default value for the <see cref="LoadOnOpen"/> property.
/// </summary>
public const bool DefaultLoadOnOpen = false;
/// <summary>
/// Specifies the default value for the <see cref="SaveOnClose"/> property.
/// </summary>
public const bool DefaultSaveOnClose = false;
/// <summary>
/// Specifies the default value for the <see cref="ReloadOnModify"/> property.
/// </summary>
public const bool DefaultReloadOnModify = false;
/// <summary>
/// Specifies the default value for the <see cref="PersistSettings"/> property.
/// </summary>
public const bool DefaultPersistSettings = false;
/// <summary>
/// Specifies the default value for the <see cref="SettingsCategory"/> property.
/// </summary>
public const string DefaultSettingsCategory = "IsamDataFile";
// Events
/// <summary>
/// Occurs when data is being read from disk into memory.
/// </summary>
public event EventHandler DataLoading;
/// <summary>
/// Occurs when data has been read from disk into memory.
/// </summary>
public event EventHandler DataLoaded;
/// <summary>
/// Occurs when data is being saved from memory onto disk.
/// </summary>
public event EventHandler DataSaving;
/// <summary>
/// Occurs when data has been saved from memory onto disk.
/// </summary>
public event EventHandler DataSaved;
/// <summary>
/// Occurs when file data on the disk is modified.
/// </summary>
public event EventHandler FileModified;
/// <summary>
/// Occurs when the class has been disposed.
/// </summary>
public event EventHandler Disposed;
// Fields
private string m_fileName;
private FileAccess m_fileAccessMode;
private int m_autoSaveInterval;
private bool m_loadOnOpen;
private bool m_saveOnClose;
private bool m_reloadOnModify;
private bool m_persistSettings;
private string m_settingsCategory;
private List<T> m_fileRecords;
private byte[] m_recordBuffer;
private FileStream m_fileData;
private readonly ManualResetEvent m_loadWaitHandle;
private readonly ManualResetEvent m_saveWaitHandle;
private readonly Timer m_autoSaveTimer;
private FileSystemWatcher m_fileWatcher;
private bool m_disposed;
private bool m_initialized;
#endregion
#region [ Constructors ]
/// <summary>
/// Initializes a new instance of the <see cref="IsamDataFileBase{T}"/> class.
/// </summary>
protected IsamDataFileBase()
{
m_fileName = DefaultFileName;
m_fileAccessMode = DefaultFileAccessMode;
m_autoSaveInterval = DefaultAutoSaveInterval;
m_loadOnOpen = DefaultLoadOnOpen;
m_saveOnClose = DefaultSaveOnClose;
m_reloadOnModify = DefaultReloadOnModify;
m_persistSettings = DefaultPersistSettings;
m_settingsCategory = DefaultSettingsCategory;
m_loadWaitHandle = new ManualResetEvent(true);
m_saveWaitHandle = new ManualResetEvent(true);
m_autoSaveTimer = new Timer();
m_autoSaveTimer.Elapsed += m_autoSaveTimer_Elapsed;
}
/// <summary>
/// Releases the unmanaged resources before the file is reclaimed by <see cref="GC"/>.
/// </summary>
~IsamDataFileBase()
{
Dispose(false);
}
#endregion
#region [ Properties ]
/// <summary>
/// Gets or sets the name of the file.
/// </summary>
/// <remarks>
/// Changing the <see cref="FileName"/> when the file is open will cause the file to be re-opend.
/// </remarks>
public string FileName
{
get
{
return m_fileName;
}
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentNullException("value");
m_fileName = value;
ReOpen();
}
}
/// <summary>
/// Gets or sets the <see cref="FileAccess"/> value to use when opening the file.
/// </summary>
public FileAccess FileAccessMode
{
get
{
return m_fileAccessMode;
}
set
{
m_fileAccessMode = value;
ReOpen();
}
}
/// <summary>
/// Gets or sets the interval in milliseconds at which the records loaded in memory are to be persisted to disk.
/// </summary>
/// <remarks>
/// <see cref="AutoSaveInterval"/> will be effective only if records have been loaded in memory either manually
/// by calling the <see cref="Load()"/> method or automatically by settings <see cref="LoadOnOpen"/> to true.
/// </remarks>
public int AutoSaveInterval
{
get
{
return m_autoSaveInterval;
}
set
{
m_autoSaveInterval = value;
ReOpen();
}
}
/// <summary>
/// Gets or sets a boolean value that indicates whether records are to be loaded automatically in memory when
/// the file is opened.
/// </summary>
public bool LoadOnOpen
{
get
{
return m_loadOnOpen;
}
set
{
m_loadOnOpen = value;
ReOpen();
}
}
/// <summary>
/// Gets or sets a boolean value that indicates whether records loaded in memory are to be persisted to disk
/// when the file is closed.
/// </summary>
/// <remarks>
/// <see cref="SaveOnClose"/> will be effective only if records have been loaded in memory either manually
/// by calling the <see cref="Load()"/> method or automatically by settings <see cref="LoadOnOpen"/> to true.
/// </remarks>
public bool SaveOnClose
{
get
{
return m_saveOnClose;
}
set
{
m_saveOnClose = value;
}
}
/// <summary>
/// Gets or sets a boolean value that indicates whether records loaded in memory are to be re-loaded when the
/// file is modified on disk.
/// </summary>
/// <remarks>
/// <see cref="ReloadOnModify"/> will be effective only if records have been loaded in memory either manually
/// by calling the <see cref="Load()"/> method or automatically by settings <see cref="LoadOnOpen"/> to true.
/// </remarks>
public bool ReloadOnModify
{
get
{
return m_reloadOnModify;
}
set
{
Lines 1–500 of 1363. Load more
//****************************************************************************************************** // IsamDataFileBase.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/eclipse-1.0.php // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 03/08/2007 - Pinal C. Patel // Original version of source code generated. // 11/30/2007 - Pinal C. Patel // Modified the "design time" check in EndInit() method to use LicenseManager.UsageMode property // instead of DesignMode property as the former is more accurate than the latter. // 09/19/2008 - J. Ritchie Carroll // Converted to C#. // 10/28/2008 - Pinal C. Patel // Edited code comments. // 12/04/2008 - J. Ritchie Carroll // Modified class an example to use new ISupportBinaryImage. // 05/12/2009 - Pinal C. Patel // Optimized Read() for better memory management by using "yield return". // 05/19/2009 - Pinal C. Patel // Implemented the IProvideStatus interface. // 07/02/2009 - Pinal C. Patel // Modified state alterning properties to reopen the file when changed. // 08/10/2009 - Pinal C. Patel // Modified Write() to write empty intermediate records that are missing to avoid garbage data // for the missing intermediate records when records are being written to disk directly. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 09/16/2009 - Pinal C. Patel // Modified Save() to flush buffered data to disk. // Removed MinimumRecordCount property - not very useful. // 09/17/2009 - Pinal C. Patel // Added exception handling to event handlers. // 12/04/2009 - Pinal C. Patel // Fixed thread synchronization bug in ReadFromDisk(). // 04/07/2011 - Pinal C. Patel // Removed inheritance from Component class to allow for serialization. // 11/23/2011 - J. Ritchie Carroll // Modified to support buffer optimized ISupportBinaryImage. // 12/06/2011 - Pinal C. Patel // Updated to instantiate a FileSystemWatcher object that watches for changes to the file content // only if needed to avoid a bug introduced in .NET 4.0 that causes memory leak. // 12/14/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Text; using System.Threading; using System.Timers; using System.Xml.Serialization; using GSF.Configuration; using GSF.Parsing; using Timer = System.Timers.Timer; namespace GSF.IO { /// <summary> /// An abstract class that defines the read/write capabilities for ISAM (Indexed Sequential Access Method) file. /// </summary> /// <typeparam name="T"> /// <see cref="Type"/> of the records the file contains. This <see cref="Type"/> must implement the <see cref="ISupportBinaryImage"/> interface. /// </typeparam> /// <remarks> /// <para> /// This ISAM implementation keeps all the records in memory, so it may not be suitable for very large files. Since data is stored /// in memory using a list, the maximum number of possible supported records will be 2,147,483,647 (i.e., Int32.MaxValue). /// </para> /// <para> /// See <a href="http://en.wikipedia.org/wiki/ISAM" target="_blank">http://en.wikipedia.org/wiki/ISAM</a> for more information on ISAM files. /// </para> /// </remarks> /// <example> /// This example shows a sample implementation of <see cref="IsamDataFileBase{T}"/>: /// <code> /// using System; /// using System.Text; /// using GSF; /// using GSF.IO; /// using GSF.Parsing; /// /// class Program /// { /// static void Main(string[] args) /// { /// // Create a few test records. /// TestIsamFileRecord r1 = new TestIsamFileRecord(1); /// r1.Name = "TestRecord1"; /// r1.Value = double.MinValue; /// r1.Description = "Test record with minimum double value"; /// TestIsamFileRecord r2 = new TestIsamFileRecord(2); /// r2.Name = "TestRecord2"; /// r2.Value = double.MaxValue; /// r2.Description = "Test record with maximum double value"; /// /// // Open ISAM file. /// TestIsamFile testFile = new TestIsamFile(); /// testFile.FileName = "TestIsamFile.dat"; /// testFile.Open(); /// /// // Write test records. /// testFile.Write(r1.Index, r1); /// testFile.Write(r2.Index, r2); /// /// // Read test records. /// Console.WriteLine(testFile.Read(1)); /// Console.WriteLine(testFile.Read(2)); /// /// // Close ISAM file. /// testFile.Close(); /// /// Console.ReadLine(); /// } /// } /// /// class TestIsamFile : IsamDataFileBase<TestIsamFileRecord> /// { /// /// <summary> /// /// Size of a single file record. /// /// </summary> /// protected override int GetRecordSize() /// { /// return TestIsamFileRecord.RecordLength; /// } /// /// /// <summary> /// /// Creates a new empty file record. /// /// </summary> /// protected override TestIsamFileRecord CreateNewRecord(int id) /// { /// return new TestIsamFileRecord(id); /// } /// } /// /// class TestIsamFileRecord : ISupportBinaryImage /// { /// private int m_index; /// private string m_name; // 20 * 1 = 20 /// private double m_value; // 1 * 8 = 8 /// private string m_description; // 100 * 1 = 100 /// /// public const int RecordLength = 128; // Total = 128 /// /// public TestIsamFileRecord(int recordIndex) /// { /// m_index = recordIndex; /// /// Name = string.Empty; /// Value = double.NaN; /// Description = string.Empty; /// } /// /// /// <summary> /// /// 1-based index of the record. /// /// </summary> /// public int Index /// { /// get { return m_index; } /// } /// /// /// <summary> /// /// Name of the record. /// /// </summary> /// public string Name /// { /// get { return m_name; } /// set { m_name = value.TruncateRight(20).PadRight(20); } /// } /// /// /// <summary> /// /// Value of the record. /// /// </summary> /// public double Value /// { /// get { return m_value; } /// set { m_value = value; } /// } /// /// /// <summary> /// /// Description of the record. /// /// </summary> /// public string Description /// { /// get { return m_description; } /// set { m_description = value.TruncateRight(100).PadRight(100); } /// } /// /// /// <summary> /// /// Serialized record length. /// /// </summary> /// public int BinaryLength /// { /// get { return RecordLength; } /// } /// /// /// <summary> /// /// Serialized record data. /// /// </summary> /// public byte[] BinaryImage /// { /// get /// { /// // Serialize TestIsamFileRecord into byte array. /// byte[] image = new byte[RecordLength]; /// Buffer.BlockCopy(Encoding.ASCII.GetBytes(Name), 0, image, 0, 20); /// Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, image, 20, 8); /// Buffer.BlockCopy(Encoding.ASCII.GetBytes(Description), 0, image, 28, 100); /// /// return image; /// } /// } /// /// /// <summary> /// /// Deserializes the record. /// /// </summary> /// public int Initialize(byte[] binaryImage, int startIndex, int length) /// { /// if (length >= RecordLength) /// { /// // Deserialize byte array into TestIsamFileRecord. /// Name = Encoding.ASCII.GetString(binaryImage, startIndex, 20); /// Value = BitConverter.ToDouble(binaryImage, startIndex + 20); /// Description = Encoding.ASCII.GetString(binaryImage, startIndex + 28, 100); /// } /// else /// throw new InvalidOperationException("Invalid record size, not enough data to deserialize record"); /// /// return RecordLength; /// } /// /// /// <summary> /// /// String representation of the record. /// /// </summary> /// public override string ToString() /// { /// return string.Format("Name: {0}, Value: {1}, Description: {2}", Name, Value, Description); /// } /// } /// </code> /// </example> public abstract class IsamDataFileBase<T> : ISupportLifecycle, IProvideStatus, IPersistSettings where T : ISupportBinaryImage { #region [ Members ] // Constants /// <summary> /// Specifies the default value for the <see cref="FileName"/> property. /// </summary> public const string DefaultFileName = "IsamDataFile.dat"; /// <summary> /// Specifies the default value for the <see cref="FileAccessMode"/> property. /// </summary> public const FileAccess DefaultFileAccessMode = FileAccess.ReadWrite; /// <summary> /// Specifies the default value for the <see cref="AutoSaveInterval"/> property. /// </summary> public const int DefaultAutoSaveInterval = -1; /// <summary> /// Specifies the default value for the <see cref="LoadOnOpen"/> property. /// </summary> public const bool DefaultLoadOnOpen = false; /// <summary> /// Specifies the default value for the <see cref="SaveOnClose"/> property. /// </summary> public const bool DefaultSaveOnClose = false; /// <summary> /// Specifies the default value for the <see cref="ReloadOnModify"/> property. /// </summary> public const bool DefaultReloadOnModify = false; /// <summary> /// Specifies the default value for the <see cref="PersistSettings"/> property. /// </summary> public const bool DefaultPersistSettings = false; /// <summary> /// Specifies the default value for the <see cref="SettingsCategory"/> property. /// </summary> public const string DefaultSettingsCategory = "IsamDataFile"; // Events /// <summary> /// Occurs when data is being read from disk into memory. /// </summary> public event EventHandler DataLoading; /// <summary> /// Occurs when data has been read from disk into memory. /// </summary> public event EventHandler DataLoaded; /// <summary> /// Occurs when data is being saved from memory onto disk. /// </summary> public event EventHandler DataSaving; /// <summary> /// Occurs when data has been saved from memory onto disk. /// </summary> public event EventHandler DataSaved; /// <summary> /// Occurs when file data on the disk is modified. /// </summary> public event EventHandler FileModified; /// <summary> /// Occurs when the class has been disposed. /// </summary> public event EventHandler Disposed; // Fields private string m_fileName; private FileAccess m_fileAccessMode; private int m_autoSaveInterval; private bool m_loadOnOpen; private bool m_saveOnClose; private bool m_reloadOnModify; private bool m_persistSettings; private string m_settingsCategory; private List<T> m_fileRecords; private byte[] m_recordBuffer; private FileStream m_fileData; private readonly ManualResetEvent m_loadWaitHandle; private readonly ManualResetEvent m_saveWaitHandle; private readonly Timer m_autoSaveTimer; private FileSystemWatcher m_fileWatcher; private bool m_disposed; private bool m_initialized; #endregion #region [ Constructors ] /// <summary> /// Initializes a new instance of the <see cref="IsamDataFileBase{T}"/> class. /// </summary> protected IsamDataFileBase() { m_fileName = DefaultFileName; m_fileAccessMode = DefaultFileAccessMode; m_autoSaveInterval = DefaultAutoSaveInterval; m_loadOnOpen = DefaultLoadOnOpen; m_saveOnClose = DefaultSaveOnClose; m_reloadOnModify = DefaultReloadOnModify; m_persistSettings = DefaultPersistSettings; m_settingsCategory = DefaultSettingsCategory; m_loadWaitHandle = new ManualResetEvent(true); m_saveWaitHandle = new ManualResetEvent(true); m_autoSaveTimer = new Timer(); m_autoSaveTimer.Elapsed += m_autoSaveTimer_Elapsed; } /// <summary> /// Releases the unmanaged resources before the file is reclaimed by <see cref="GC"/>. /// </summary> ~IsamDataFileBase() { Dispose(false); } #endregion #region [ Properties ] /// <summary> /// Gets or sets the name of the file. /// </summary> /// <remarks> /// Changing the <see cref="FileName"/> when the file is open will cause the file to be re-opend. /// </remarks> public string FileName { get { return m_fileName; } set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException("value"); m_fileName = value; ReOpen(); } } /// <summary> /// Gets or sets the <see cref="FileAccess"/> value to use when opening the file. /// </summary> public FileAccess FileAccessMode { get { return m_fileAccessMode; } set { m_fileAccessMode = value; ReOpen(); } } /// <summary> /// Gets or sets the interval in milliseconds at which the records loaded in memory are to be persisted to disk. /// </summary> /// <remarks> /// <see cref="AutoSaveInterval"/> will be effective only if records have been loaded in memory either manually /// by calling the <see cref="Load()"/> method or automatically by settings <see cref="LoadOnOpen"/> to true. /// </remarks> public int AutoSaveInterval { get { return m_autoSaveInterval; } set { m_autoSaveInterval = value; ReOpen(); } } /// <summary> /// Gets or sets a boolean value that indicates whether records are to be loaded automatically in memory when /// the file is opened. /// </summary> public bool LoadOnOpen { get { return m_loadOnOpen; } set { m_loadOnOpen = value; ReOpen(); } } /// <summary> /// Gets or sets a boolean value that indicates whether records loaded in memory are to be persisted to disk /// when the file is closed. /// </summary> /// <remarks> /// <see cref="SaveOnClose"/> will be effective only if records have been loaded in memory either manually /// by calling the <see cref="Load()"/> method or automatically by settings <see cref="LoadOnOpen"/> to true. /// </remarks> public bool SaveOnClose { get { return m_saveOnClose; } set { m_saveOnClose = value; } } /// <summary> /// Gets or sets a boolean value that indicates whether records loaded in memory are to be re-loaded when the /// file is modified on disk. /// </summary> /// <remarks> /// <see cref="ReloadOnModify"/> will be effective only if records have been loaded in memory either manually /// by calling the <see cref="Load()"/> method or automatically by settings <see cref="LoadOnOpen"/> to true. /// </remarks> public bool ReloadOnModify { get { return m_reloadOnModify; } set {