在Windows Azure 中,数据的存储主要有三种格式:Blob,TableStorage, SqlAzure。在本节中,主要介绍如何将Entity存入TableStorage 中,或者将TableStorage的数据转换成相应的Entity。
一个TableStorage其实就是一个数据表,在该表中,有两个关键字段:PartitionKey 和 RowKey,这两个字段要能够唯一确定一条记录。
下边首先讲如何将对应的Entity 数据存入TableStorage中。
要想将Entity存入TableStorage中,这个Entity必须继承自:TableServiceEntity 类。
下边是TableServiceEntity类的代码结构:
namespace Microsoft.WindowsAzure.StorageClient { // Summary: // Represents an entity in the Windows Azure Table service. [CLSCompliant(false)] public abstract class TableServiceEntity { // Summary: // Initializes a new instance of the Microsoft.WindowsAzure.StorageClient.TableServiceEntity // class. protected TableServiceEntity(); // // Summary: // Initializes a new instance of the Microsoft.WindowsAzure.StorageClient.TableServiceEntity // class. // // Parameters: // partitionKey: // The partition key. // // rowKey: // The row key. protected TableServiceEntity(string partitionKey, string rowKey); // Summary: // Gets or sets the partition key of a table entity. public virtual string PartitionKey { get; set; } // // Summary: // Gets or sets the row key of a table entity. public virtual string RowKey { get; set; } // // Summary: // Gets or sets the timestamp for the entity. public DateTime Timestamp { get; set; } } }
从代码中可以看到,这个类有三个字段 PartitionKey,RowKey,Timestamp。这三个字段和tableStorage中的字段是一一对应的。如果一个实体要保存到 TableStorage中,PartitionKey,RowKey不能为空,且不能和TableStorage中已有的记录重复。
下边我们 写一个类 让其继承自 TableServiceEntity
namespace RF.Integration.Common { public class LogBase : TableServiceEntity { private string externalSystemName; private string batchNum; private int pageCount; private int pageIndex; public string ExternalSystemName { get { return externalSystemName; } set { externalSystemName = value; } } public string BatchNum { get { return batchNum; } set { batchNum = value; } } public int PageCount { get { return pageCount; } set { pageCount = value; } } public int PageIndex { get { return pageIndex; } set { pageIndex = value; } } } }
在我的实际项目中,LogBase类是一个父类,其下边有三个子类。如下的UploadLogInfo是其中的一个子类
namespace RF.Integration.Common { public class UploadLogInfo : LogBase { private string fileSchemaVersion; private string fileName; private string fileSize; private string saveName; private string startUploadTime; private string endUploadTime; private string endStatus; private string errorInfo; public string FileSchemaVersion { get { return fileSchemaVersion; } set { fileSchemaVersion = value;} } public string FileName { get { return fileName; } set { fileName = value; } } public string FileSize { get { return fileSize; } set { fileSize = value; } } public string SaveName { get { return saveName;} set { saveName = value; } } public string StartUploadTime { get { return startUploadTime;} set { startUploadTime = value;} } public string EndUploadTime { get { return endUploadTime; } set { endUploadTime=value; } } public string EndStatus { get { return endStatus; } set { endStatus = value; } } public string ErrorInfo { get { return errorInfo; } set { errorInfo = value;} } public void InitUploadLogInfo(RemoteFileInfo remoteFileInfo) { this.ExternalSystemName = remoteFileInfo.externalSystemName.ToLower(); this.FileSchemaVersion = remoteFileInfo.SchemaVersion; this.FileName = remoteFileInfo.fileName; this.BatchNum = remoteFileInfo.batchInfo.ToString("N"); this.PageCount =int.Parse(remoteFileInfo.pageCount); this.PageIndex =int.Parse(remoteFileInfo.pageIndex); } } }
下边向大家提供一个LogProvider类,用于将上述所说的Log类信息保存到 TableStorage中
namespace RF.Integration.Common { public class LogProvider { TableHelper tableHelper; private static LogProvider logProvider; private LogProvider() { #if DEBUG tableHelper = new TableHelper(); #else tableHelper = new TableHelper(ConstLib.STORAGE_CONNECTION_STR,true); #endif } public static LogProvider CreatInstance() { object _lock = new object(); if (logProvider == null) { lock (_lock) { if (logProvider == null) { logProvider = new LogProvider(); } } } return logProvider; } public void SaveLog<T>(T logInfo) where T : LogBase { string tableName=""; if (logInfo is UploadLogInfo) { tableName=ConstLib.AZURE_UPLOADLOG_TABLE; } if(logInfo is CompareLogInfo) { tableName=ConstLib.AZURE_COMPARELOG_TABLE; } if (logInfo is DownLoadLogInfo) { tableName = ConstLib.AZURE_DownLoadLog_TABLE; } if (String.IsNullOrEmpty(logInfo.PartitionKey)) { logInfo.PartitionKey = logInfo.ExternalSystemName; } if (String.IsNullOrEmpty(logInfo.RowKey)) { logInfo.RowKey = String.Format("{0}-{1}-[{2}-{3}]", logInfo.BatchNum,DateTime.Now.ToString("yyyyMMddHHmmss"), logInfo.PageCount, logInfo.PageIndex); } if (!string.IsNullOrEmpty(tableName) && !string.IsNullOrEmpty(logInfo.PartitionKey) && !string.IsNullOrEmpty(logInfo.RowKey)) { if (GetLogInfo<T>(logInfo) == null) { tableHelper.InsertEntity(tableName, logInfo); } else { tableHelper.ReplaceUpdateEntity<T>(tableName, logInfo.PartitionKey, logInfo.RowKey, logInfo); } } } public T GetLogInfo<T>(T logInfo) where T : TableServiceEntity { string tableName = ""; if (logInfo is UploadLogInfo) { tableName = ConstLib.AZURE_UPLOADLOG_TABLE; } if (logInfo is CompareLogInfo) { tableName = ConstLib.AZURE_COMPARELOG_TABLE; } if (logInfo is DownLoadLogInfo) { tableName = ConstLib.AZURE_DownLoadLog_TABLE; } TableHelper tableHelper; #if DEBUG tableHelper = new TableHelper(); #else tableHelper = new TableHelper(ConstLib.STORAGE_CONNECTION_STR,true); #endif T rtnLogInfo = tableHelper.QueryEntitiesByPartitionAndRowKey<T>(tableName, logInfo.PartitionKey, logInfo.RowKey).FirstOrDefault(); return rtnLogInfo; } } }
从代码中可以看到,该类提供两个方法,SaveLog 和 GetLogInfo,分别用来存储和读取log信息。
其中引用到的TableHelper类中的两个方法如下,这也是我们这节的核心方法
public bool ReplaceUpdateEntity<T>(string tableName, string partitionKey, string rowKey, T obj) where T : TableServiceEntity { try { TableServiceContext tableServiceContext = TableClient.GetDataServiceContext(); tableServiceContext.IgnoreResourceNotFoundException = true; IQueryable<T> entities = (from e in tableServiceContext.CreateQuery<T>(tableName) where e.PartitionKey == partitionKey && e.RowKey == rowKey select e); T entity = entities.FirstOrDefault(); Type t = obj.GetType(); PropertyInfo[] pi = t.GetProperties(); foreach (PropertyInfo p in pi) { p.SetValue(entity, p.GetValue(obj, null), null); } tableServiceContext.UpdateObject(entity); tableServiceContext.SaveChanges(SaveChangesOptions.ReplaceOnUpdate); return true; } catch (DataServiceRequestException) { return false; } catch (StorageClientException ex) { if ((int)ex.StatusCode == 404) { return false; } throw; } }
public List<T> QueryEntitiesByPartitionAndRowKey<T>(string tableName, string partitionKey, string rowKey) where T : TableServiceEntity { CreateTableIfNotExist(tableName); TableServiceContext tableServiceContext = TableClient.GetDataServiceContext(); tableServiceContext.IgnoreResourceNotFoundException = true; List<T> entities = new List<T>(); try { entities = (from e in tableServiceContext.CreateQuery<T>(tableName) where e.PartitionKey == partitionKey && e.RowKey == rowKey select e).AsTableServiceQuery<T>().ToList(); } catch { } return entities; }
另外需要注意的是:PartitionKey,RowKey中的值不能包含 “/”