【Silverlight】利用IsolatedStorageFile實現客戶端快取

iDotNetSpace發表於2009-07-31

為將一套系統區域性改版為BS應用,最近在琢磨Silverlight,發現這玩意可能真能帶來一場革命,對於程式設計師來說比Flash好的還真不是一星半點。

廢話就不說了,來點實在的。我們有些資料,特別是些不是經常變的資料往往需要進行快取,以往在asp.net的時候使用page的cache屬性,其實那還是存放在伺服器端,對伺服器壓力比較大,這下好了silverlight提供了IsolatedStorageFile,而且可以申請無限量空間,當然是要人家客戶端同意了,實現也很簡單

【Silverlight】利用IsolatedStorageFile實現客戶端快取
        /// 
        
/// 在客戶端申請儲存空間
        
/// 
        
/// 空間大小(KB)
        
/// 
        public static bool ApplyStorageSpace(double size)
        {
            
try
            {
                
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    Int64 newQuotaSize 
= Convert.ToInt64(size * 1024); //換算為位元組
                    Int64 curAvail = store.AvailableFreeSpace;

                    
if (curAvail < newQuotaSize)
                    {
                        
if (!store.IncreaseQuotaTo(newQuotaSize))
                            
return false;
                        
else
                            
return true;
                    }
                    
else
                    {
                        
return true;
                    }
                }
            }
            
catch (IsolatedStorageException ex)
            {
                
throw ex;
            }
        }

看到這個東西自然而然就想到了,用來做快取和離線資料儲存肯定能成,於是找了下老外的做法(silverlight還是老外用的多啊)找個個不錯的類,簡單改了改就很好用了

【Silverlight】利用IsolatedStorageFile實現客戶端快取
/// 
    
/// Client Storage class for storing objects in IsolatedStorage
    
/// 
    public class ClientStorage
    {
        
#region Constants

        
const string ISOLATED_KEY_FILE_NAME = "KeyNames.txt";
        
const string KEY_OBJECT_FILE = "object.xml";

        
#endregion

        
#region Private Static Fields

        IsolatedStorageFile isoStore 
= IsolatedStorageFile.GetUserStoreForApplication();
        Dictionary
<string, TypeAndValue> keysNTypes;

        
#endregion

        
#region CTOR
        
/// 
        
/// private CTOR to initialize the class to make it singleton
        
/// 
        private ClientStorage()
        {
            keysNTypes 
= new Dictionary<string, TypeAndValue>();
            
if (FileExists(ISOLATED_KEY_FILE_NAME))
            {
                ReadKeys(isoStore);
            }
        }

        
/// 
        
/// Nested class for lazy initialization.
        
/// 
        class NestedClientStorage
        {
            
// Explicit static constructor to tell C# compiler
            
// not to mark type as beforefieldinit
            static NestedClientStorage()
            {
            }
            
internal static readonly ClientStorage Instance = new ClientStorage();
        }
        
#endregion

        
#region Private Helper Methods

        
private void ReadKeys(IsolatedStorageFile isoStore)
        {
            IsolatedStorageFileStream iStream 
= new IsolatedStorageFileStream(ISOLATED_KEY_FILE_NAME,
                                                                    FileMode.Open, isoStore);
            DataContractSerializer serializer 
= new DataContractSerializer(keysNTypes.GetType());
            keysNTypes 
= serializer.ReadObject(iStream) as Dictionary<string, TypeAndValue>;
            iStream.Close();
        }

        
private void AddKey(string key, object value)
        {
            
if (!keysNTypes.ContainsKey(key))
                keysNTypes.Add(key, 
new TypeAndValue());
            keysNTypes[key].TypeofObject 
= value.GetType();
            keysNTypes[key].StoredObject 
= value;
            WriteKeyFile();
        }

        
private void WriteKeyFile()
        {
            
using (IsolatedStorageFileStream oStream = new IsolatedStorageFileStream(ISOLATED_KEY_FILE_NAME,
                                                        FileMode.Create, isoStore))
            {
                DataContractSerializer serializer 
= new DataContractSerializer(keysNTypes.GetType());

                serializer.WriteObject(oStream, keysNTypes);
                oStream.Close();
            }
        }

        
private object Retreive(string key)
        {
            
object value = null;

            
if (FileExists(key + KEY_OBJECT_FILE) && keysNTypes.ContainsKey(key))
            {
                
if (keysNTypes[key].StoredObject == null)
                {
                    
try
                    {
                        
using (IsolatedStorageFileStream iStream = new IsolatedStorageFileStream(key + KEY_OBJECT_FILE, FileMode.OpenOrCreate, isoStore))
                        {
                            
if (iStream != null)
                            {
                                
try
                                {
                                    DataContractSerializer serializer 
= new DataContractSerializer(keysNTypes[key].TypeofObject);
                                    value 
= serializer.ReadObject(iStream);
                                }
                                
catch (Exception)
                                {
                                    
// Do nothing simply retrun null
                                }
                                keysNTypes[key].StoredObject 
= value;
                                iStream.Close();
                            }
                        }
                    }
                    
catch (FileNotFoundException)
                    {
                        
throw new KeyNotFoundException();
                    }
                }
                
else
                {
                    value 
= keysNTypes[key].StoredObject;
                }
            }
            
return value;
        }

        
private void AddOrUpdate(string key, object value)
        {
            
try
            {
                IsolatedStorageFileStream oStream 
= new IsolatedStorageFileStream(key + KEY_OBJECT_FILE,
                                                    FileMode.Create, isoStore);
                DataContractSerializer serializer 
= new DataContractSerializer(value.GetType());

                serializer.WriteObject(oStream, value);

                oStream.Close();
            }
            
catch (IsolatedStorageException)
            {
                
if (ApplyStorageSpace(1024 * 1024 * 1024))
                {
                    AddOrUpdate(key, value);
                }
            }
            
        }

        
private void Add(string key, object value, bool throwErrorOnDuplicate)
        {
            
if (keysNTypes.ContainsKey(key) && throwErrorOnDuplicate)
            {
                
throw new System.Exception("Duplicate key provided.");
            }
            
else
            {
                AddKey(key, value);
                AddOrUpdate(key, value);
            }
        }

        
private bool FileExists(string fileName)
        {
            
return isoStore.FileExists(fileName);
        }
        
#endregion

        
#region Public Methods
        
/// 
        
/// Public static property to get the instance of ClientStorage which is a singleton class
        
/// 
        public static ClientStorage Instance
        {
            
get
            {
                
return NestedClientStorage.Instance;
            }
        }

        
///  
        
/// Adds a key/value to the storage device. 
        
///  
        
/// Key to identify the object 
        
/// Version Number
        
/// Value as object 
        public void Add(string key, object value)
        {
            Add(key, value, 
true);
        }

        
/// 
        
/// Remove a element from the Isolated Storage
        
/// 
        
/// key
        public void Remove(string key)
        {
            keysNTypes.Remove(key);
            WriteKeyFile();
            
if (FileExists(key + KEY_OBJECT_FILE))
            {
                isoStore.DeleteFile(key 
+ KEY_OBJECT_FILE);
            }

        }

        
/// 
        
/// Indexer for CLientStorage
        
/// 
        
/// Key
        
///  Version Number
        
/// returns the object on the basis of key
        public object this[string key]
        {
            
get
            {
                
return Retreive(key);
            }
            
set
            {
                Add(key, value, 
false);
            }
        }

        
/// 
        
/// 在客戶端申請儲存空間
        
/// 
        
/// 空間大小(KB)
        
/// 
        public static bool ApplyStorageSpace(double size)
        {
            
try
            {
                
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    Int64 newQuotaSize 
= Convert.ToInt64(size * 1024); //換算為位元組
                    Int64 curAvail = store.AvailableFreeSpace;

                    
if (curAvail < newQuotaSize)
                    {
                        
if (!store.IncreaseQuotaTo(newQuotaSize))
                            
return false;
                        
else
                            
return true;
                    }
                    
else
                    {
                        
return true;
                    }
                }
            }
            
catch (IsolatedStorageException ex)
            {
                
throw ex;
            }
        }
        
#endregion

        
#region TypeAndValue

        [DataContract]
        
public class TypeAndValue
        {
            
public TypeAndValue()
            {
            }

            
public Type TypeofObject { getset; }
            
public object StoredObject { getset; }

            
private string fullyQualifiedTypeName;

            [DataMember]
            
public string FullyQualifiedTypeName
            {
                
get
                {
                    
if (fullyQualifiedTypeName == null)
                    {
                        fullyQualifiedTypeName 
= TypeofObject.AssemblyQualifiedName;
                    }
                    
return fullyQualifiedTypeName;
                }
                
set
                {
                    fullyQualifiedTypeName 
= value;
                    TypeofObject 
= Type.GetType(fullyQualifiedTypeName);
                }
            }
        }
        
#endregion
    }

本來用來做快取這個就夠用了,但為了能進一步使用方便,還是做了件多餘的事就是在這個基礎上再封了個Cache類這樣用起來可能就更直觀了

【Silverlight】利用IsolatedStorageFile實現客戶端快取

namespace SilverlightCache
{
    
public class Cache
    {
        Cache() 
        {
        }

        
/// 
        
/// Nested class for lazy initialization.
        
/// 
        class NestedCache
        {
            
// Explicit static constructor to tell C# compiler
            
// not to mark type as beforefieldinit
            static NestedCache()
            {
            }
            
internal static readonly Cache Instance = new Cache();
        }

        
public static Cache Instance
        {
            
get { return NestedCache.Instance; }
        }

        
public object this[string key]
        {
            
get { return ClientStorage.Instance[key]; }
            
set { ClientStorage.Instance[key] = value; }
        }

        
public void Remove(string key)
        {
            ClientStorage.Instance.Remove(key);
        }

    }
}

用法也超簡單,例程假設的是從服務取資料,然後快取到客戶端,下次使用時不再訪問服務即可,當然如果使用者清理了IsolatedStorage就要重新取服務上的資料了

【Silverlight】利用IsolatedStorageFile實現客戶端快取
public partial class MainPage : UserControl
    {
        Service1Client client 
= new Service1Client();
        
const string KEY_CACHE = "key_cache";

        
public MainPage()
        {
            InitializeComponent();
            client.DoWorkCompleted 
+= new EventHandler<DoWorkCompletedEventArgs>(client_DoWorkCompleted);
        }

        
void client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)
        {
            Cache.Instance[KEY_CACHE] 
= e.Result;
            Message(
"Service""List's count:"+e.Result.Count().ToString());
        }

        
private void btnTest_Click(object sender, RoutedEventArgs e)
        {
            
if (Cache.Instance[KEY_CACHE] == null)
            {
                client.DoWorkAsync();
            }
            
else
            {
                Message(
"Cache""List's count:" + (Cache.Instance[KEY_CACHE] as ObservableCollection<Something>).Count().ToString());
            }
        }

        
void Message(string from, string msg)
        {
            MessageBox.Show(
string.Format("Data from {0}, msg is {1}", from, msg));
        }
    }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-610957/,如需轉載,請註明出處,否則將追究法律責任。

相關文章