Monday, May 26, 2008

SQLite Dataset Helper Class



namespace LiuHarry.Utils.DB
{
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Runtime.InteropServices;
using System.Text;

public class SqliteDataSetHelper : IDisposable
{
private readonly string DELETED_FLAG;
private bool disposed;
private readonly string INSERTED_FLAG;
private string m_ConnectionString;
private bool m_LogSqlExecute;
private bool m_SelfBuildConnection;
private SQLiteConnection m_SqlConn;
private SqlExecuteTracer m_SqlExecuteTracer;
private readonly string MODIFIED_FLAG;
private readonly string PARAMETER_NAME_PREFIX;
private readonly string PARAMETER_PREFIX;

public SqliteDataSetHelper()
{
this.m_LogSqlExecute = false;
this.m_SelfBuildConnection = true;
this.DELETED_FLAG = "DELETED_FLAG";
this.INSERTED_FLAG = "INSERTED_FLAG";
this.MODIFIED_FLAG = "MODIFIED_FLAG";
this.PARAMETER_PREFIX = "@";
this.PARAMETER_NAME_PREFIX = "@";
this.disposed = false;
this.m_SqlExecuteTracer = new SqlExecuteTracer();
}

public SqliteDataSetHelper(SQLiteConnection conn) : this()
{
if (conn != null)
{
this.m_SelfBuildConnection = false;
this.m_SqlConn = conn;
}
}

public SqliteDataSetHelper(string connectionString) : this()
{
this.m_ConnectionString = connectionString;
}

private void ActivateConnection()
{
if (this.m_SqlConn == null)
{
this.m_SqlConn = new SQLiteConnection(this.m_ConnectionString);
this.m_SelfBuildConnection = true;
}
}

public void DeleteRows(List<DataRow> listRows, string tableName, string keyFieldName)
{
foreach (DataRow row in listRows)
{
this.SetToDeleteState(row);
this.InnerDeleteRow(row, tableName, keyFieldName);
}
}

public void DeleteRows(List<long> listRowKeyFieldValue, string tableName, string keyFieldName)
{
foreach (int num in listRowKeyFieldValue)
{
this.InnerDeleteRowByKeyField(num, tableName, keyFieldName, typeof(long));
}
}

public void DeleteRows(List<string> listRowKeyFieldValue, string tableName, string keyFieldName)
{
foreach (string str in listRowKeyFieldValue)
{
this.InnerDeleteRowByKeyField(str, tableName, keyFieldName, typeof(string));
}
}

public void DeleteRows(DataRow row, string tableName, string keyFieldName)
{
this.SetToDeleteState(row);
this.InnerDeleteRow(row, tableName, keyFieldName);
}

public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (!this.disposed)
{
this.TryCloseConnection();
}
this.disposed = true;
}

public int ExecCommand(SQLiteCommand sqlcom)
{
int num2;
this.ActivateConnection();
sqlcom.Connection = this.m_SqlConn;
try
{
this.TryOpenConnection();
this.m_SqlExecuteTracer.TraceSqlToLog(sqlcom);
num2 = sqlcom.ExecuteNonQuery();
}
catch (Exception exception)
{
this.m_SqlExecuteTracer.TraceErrorSqlToLog(sqlcom, exception.Message);
throw exception;
}
finally
{
this.TryCloseConnection();
}
return num2;
}

public int ExecCommand(string sql)
{
if (sql.EndsWith(","))
{
sql = sql.Substring(0, sql.Length - 1);
}
SQLiteCommand sqlcom = new SQLiteCommand(sql);
return this.ExecCommand(sqlcom);
}

public SQLiteDataReader ExecDataReader(SQLiteCommand sqlcom)
{
SQLiteDataReader reader;
this.ActivateConnection();
sqlcom.Connection = this.m_SqlConn;
try
{
this.TryOpenConnection();
this.m_SqlExecuteTracer.TraceSqlToLog(sqlcom);
reader = sqlcom.ExecuteReader();
}
catch (Exception exception)
{
this.m_SqlExecuteTracer.TraceErrorSqlToLog(sqlcom, exception.Message);
throw exception;
}
finally
{
this.TryCloseConnection();
}
return reader;
}

public SQLiteDataReader ExecDataReader(string sqlSelect)
{
if (sqlSelect.EndsWith(","))
{
sqlSelect = sqlSelect.Substring(0, sqlSelect.Length - 1);
}
SQLiteCommand sqlcom = new SQLiteCommand(sqlSelect);
return this.ExecDataReader(sqlcom);
}

public DataTable ExecDataTable(SQLiteCommand sqlcom)
{
DataTable table2;
DataTable dataTable = new DataTable();
SQLiteDataAdapter adapter = new SQLiteDataAdapter(sqlcom);
this.ActivateConnection();
sqlcom.Connection = this.m_SqlConn;
try
{
this.TryOpenConnection();
this.m_SqlExecuteTracer.TraceSqlToLog(sqlcom);
adapter.Fill(dataTable);
table2 = dataTable;
}
catch (Exception exception)
{
this.m_SqlExecuteTracer.TraceErrorSqlToLog(sqlcom, exception.Message);
throw exception;
}
finally
{
this.TryCloseConnection();
}
return table2;
}

public DataTable ExecDataTable(string sqlSelect)
{
if (sqlSelect.EndsWith(","))
{
sqlSelect = sqlSelect.Substring(0, sqlSelect.Length - 1);
}
SQLiteCommand sqlcom = new SQLiteCommand(sqlSelect);
return this.ExecDataTable(sqlcom);
}

public object ExecScalar(SQLiteCommand sqlcom)
{
object obj2;
this.ActivateConnection();
sqlcom.Connection = this.m_SqlConn;
try
{
this.TryOpenConnection();
this.m_SqlExecuteTracer.TraceSqlToLog(sqlcom);
obj2 = sqlcom.ExecuteScalar();
}
catch (Exception exception)
{
this.m_SqlExecuteTracer.TraceErrorSqlToLog(sqlcom, exception.Message);
throw exception;
}
finally
{
this.TryCloseConnection();
}
return obj2;
}

public object ExecScalar(string sqlSelect)
{
if (sqlSelect.EndsWith(","))
{
sqlSelect = sqlSelect.Substring(0, sqlSelect.Length - 1);
}
SQLiteCommand sqlcom = new SQLiteCommand(sqlSelect);
return this.ExecScalar(sqlcom);
}

~SqliteDataSetHelper()
{
this.Dispose(false);
}

public DateTime GetDbTime()
{
string commandText = "select datetime('NOW') ";
SQLiteCommand sqlcom = new SQLiteCommand(commandText);
return (DateTime) this.ExecScalar(sqlcom);
}

private DbType GetDbType(Type type)
{
DbType guid = DbType.String;
if (type.Equals(typeof(int)) || type.IsEnum)
{
return DbType.Int32;
}
if (type.Equals(typeof(long)))
{
return DbType.Int32;
}
if (type.Equals(typeof(double)) || type.Equals(typeof(double)))
{
return DbType.Decimal;
}
if (type.Equals(typeof(DateTime)))
{
return DbType.DateTime;
}
if (type.Equals(typeof(bool)))
{
return DbType.Boolean;
}
if (type.Equals(typeof(string)))
{
return DbType.String;
}
if (type.Equals(typeof(decimal)))
{
return DbType.Decimal;
}
if (type.Equals(typeof(byte[])))
{
return DbType.Binary;
}
if (type.Equals(typeof(Guid)))
{
guid = DbType.Guid;
}
return guid;
}

public long GetMaxID(string primaryKeyField, string tableName)
{
SQLiteCommand sqlcom = new SQLiteCommand("Select Max(" + primaryKeyField + ") from " + tableName);
return (long) this.ExecScalar(sqlcom);
}

private void InnerDeleteRow(DataRow dr, string TableName, string keyFieldName)
{
string format = "Delete from {0} where {1} =" + this.PARAMETER_PREFIX + "{1}";
DataTable table = dr.Table;
SQLiteCommand sqlcom = new SQLiteCommand(string.Format(format, TableName, keyFieldName));
IDataParameter parameter = new SQLiteParameter();
parameter.ParameterName = this.PARAMETER_NAME_PREFIX + keyFieldName;
parameter.DbType = this.GetDbType(table.Columns[keyFieldName].DataType);
parameter.Value = dr[keyFieldName];
sqlcom.Parameters.Add(parameter);
this.ExecCommand(sqlcom);
}

private void InnerDeleteRowByKeyField(object RowKeyFieldValue, string tableName, string keyFieldName, Type keyFieldType)
{
SQLiteCommand sqlcom = new SQLiteCommand(string.Format("Delete from {0} where {1} =" + this.PARAMETER_PREFIX + "{1}", tableName, keyFieldName));
IDataParameter parameter = new SQLiteParameter();
parameter.ParameterName = this.PARAMETER_NAME_PREFIX + keyFieldName;
parameter.DbType = this.GetDbType(keyFieldType);
parameter.Value = RowKeyFieldValue;
sqlcom.Parameters.Add(parameter);
this.ExecCommand(sqlcom);
}

private void InnerInsertRow(DataRow dr, string TableName, string primaryKeyField, bool primaryKeyValueIsAutoGenerated, out long primaryKeyValue)
{
string format = "Insert into {0}({1}) values ({2})";
SQLiteCommand sqlcom = new SQLiteCommand();
DataTable table = dr.Table;
StringBuilder builder = new StringBuilder();
StringBuilder builder2 = new StringBuilder();
for (int i = 0; i < dr.Table.Columns.Count; i++)
{
if (!primaryKeyValueIsAutoGenerated || !(table.Columns[i].ColumnName == primaryKeyField))
{
IDataParameter parameter = new SQLiteParameter();
parameter.ParameterName = this.PARAMETER_NAME_PREFIX + table.Columns[i].ColumnName;
parameter.DbType = this.GetDbType(table.Columns[i].DataType);
parameter.Value = dr[i];
sqlcom.Parameters.Add(parameter);
builder2.Append(table.Columns[i].ColumnName);
builder.Append(this.PARAMETER_PREFIX + table.Columns[i].ColumnName);
builder2.Append(",");
builder.Append(",");
}
}
string str2 = builder2.ToString();
str2 = str2.Substring(0, str2.Length - 1);
string str3 = builder.ToString();
str3 = str3.Substring(0, str3.Length - 1);
string str4 = string.Format(format, TableName, str2, str3);
sqlcom.CommandText = str4;
this.ExecCommand(sqlcom);
if (!primaryKeyValueIsAutoGenerated)
{
primaryKeyValue = 0L;
}
else
{
SQLiteCommand command2 = new SQLiteCommand("Select Max(" + primaryKeyField + ") from " + TableName);
object obj2 = this.ExecScalar(command2);
primaryKeyValue = (long) obj2;
}
}

private void InnerModifyRow(DataRow dr, string TableName, string keyFieldName)
{
string format = "Update {0} set {1} {2}";
string str2 = "{0}= " + this.PARAMETER_PREFIX + "{0}";
string str3 = " Where {0}=" + this.PARAMETER_PREFIX + "{0}";
StringBuilder builder = new StringBuilder();
SQLiteCommand sqlcom = new SQLiteCommand();
DataTable table = dr.Table;
for (int i = 0; i < dr.Table.Columns.Count; i++)
{
IDataParameter parameter = new SQLiteParameter();
parameter.ParameterName = this.PARAMETER_NAME_PREFIX + table.Columns[i].ColumnName;
parameter.DbType = this.GetDbType(table.Columns[i].DataType);
parameter.Value = dr[i];
sqlcom.Parameters.Add(parameter);
if (table.Columns[i].ColumnName == keyFieldName)
{
str3 = string.Format(str3, keyFieldName);
}
else
{
builder.Append(string.Format(str2, table.Columns[i].ColumnName));
builder.Append(",");
}
}
string str4 = builder.ToString();
str4 = str4.Substring(0, str4.Length - 1);
string str5 = string.Format(format, TableName, str4, str3);
sqlcom.CommandText = str5;
this.ExecCommand(sqlcom);
}

public void InsertRows(List<DataRow> listRows, string tableName)
{
foreach (DataRow row in listRows)
{
this.InsertRows(row, tableName);
}
}

public void InsertRows(DataRow row, string tableName)
{
long num;
this.SetToInsertState(row);
string primaryKeyField = "";
bool primaryKeyValueIsAutoGenerated = false;
this.InnerInsertRow(row, tableName, primaryKeyField, primaryKeyValueIsAutoGenerated, out num);
}

public void InsertRows(List<DataRow> listRows, string tableName, string primaryKeyField, bool primaryKeyValueIsAutoGenerated, out List<long> listPrimaryKeyValue)
{
listPrimaryKeyValue = new List<long>();
foreach (DataRow row in listRows)
{
long num;
this.SetToInsertState(row);
this.InnerInsertRow(row, tableName, primaryKeyField, primaryKeyValueIsAutoGenerated, out num);
listPrimaryKeyValue.Add(num);
}
}

public void InsertRows(DataRow row, string tableName, string primaryKeyField, bool primaryKeyValueIsAutoGenerated, out long primaryKeyValue)
{
this.SetToInsertState(row);
this.InnerInsertRow(row, tableName, primaryKeyField, primaryKeyValueIsAutoGenerated, out primaryKeyValue);
}

public void ModifyRows(List<DataRow> listRows, string tableName, string keyFieldName)
{
foreach (DataRow row in listRows)
{
this.SetToModifyState(row);
this.InnerModifyRow(row, tableName, keyFieldName);
}
}

public void ModifyRows(DataRow row, string tableName, string keyFieldName)
{
this.SetToModifyState(row);
this.InnerModifyRow(row, tableName, keyFieldName);
}

public void SetToDeleteState(DataRow row)
{
row.RowError = this.DELETED_FLAG;
}

public void SetToInsertState(DataRow row)
{
row.RowError = this.INSERTED_FLAG;
}

public void SetToModifyState(DataRow row)
{
row.RowError = this.MODIFIED_FLAG;
}

private void TryCloseConnection()
{
if (this.m_SelfBuildConnection && (this.m_SqlConn != null))
{
this.m_SqlConn.Close();
}
}

private void TryOpenConnection()
{
if (this.m_SelfBuildConnection)
{
this.m_SqlConn.Open();
}
}

public string ConnectionString
{
get
{
return this.m_ConnectionString;
}
set
{
this.m_ConnectionString = value;
}
}

public bool LogSqlExecute
{
get
{
return this.m_LogSqlExecute;
}
set
{
this.m_LogSqlExecute = value;
this.m_SqlExecuteTracer.LogSqlExecute = this.m_LogSqlExecute;
}
}
}
}

Managed ExecuteShell API



namespace LiuHarry.Utils.Foundation
{
using System;
using System.Diagnostics;

public class CommandShell
{
public static string RunCmd(string command)
{
Process process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/c " + command;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
return process.StandardOutput.ReadToEnd();
}

public static void CallWindowsApp()
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
//call calculator
proc.StartInfo.FileName="calc";
proc.Start();
}

public static void GotoWebSite()
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.FileName="iexplore";
proc.StartInfo.Arguments=http://www.microsoft.com;
proc.Start();
proc.WaitForExit();
MessageBox.Show("You have just visited www.microsoft.com");
}

public static void OpenWordDocument()
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.FileName="winword";
proc.StartInfo.Arguments="C:\\Dotnetstuff\\TestWordDoc.doc";
proc.Start();
}

public static void ExecuteBatchFile()
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.FileName="c:\\dotnetstuff\\netdrv.bat";
proc.Start();
MessageBox.Show("Map Drive Created");
}

}


}

How to create a new XmlElement

XmlNode是一个Context相关的对象, 你不能直接调用XmlNode的构造子来创建一个XmlNode. 要创建一个XmlNode有两个方法. 假设要在xmlParentNode下创建一个newNode.
方法1: 通过XmlDocument的CreateElement方法创建一个XmlNode
  public XmlElement CreateElement(prefix,localName,namespaceURI),如果元素带有namespace,必须加上namespaceURI, 比如:
步骤1:XmlElement newNode= docNode.CreateElement("w", "br", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
步骤2:xmlParentNode.InsertAfter(br, rNode.FirstChild);

方法2: 不是直接创建XmlElement,而是通过修改xmlParentNode的InnerXml文本, 来增加一个节点.记住:如果新加的内容包含前缀,一定要加上namespace URI.否则会报错的.  

xmlParentNode.InnerXml= @"<w:r xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main""><w:rPr>
</w:rPr>
<w:br/>
<w:t>Some string </w:t></w:r>"
;

Wednesday, May 21, 2008

Thread safe Singleton pattern

关于Singleton模式的几个误区
网上有很多Singleton模式的样板, 多数是错误的, 下面的文档提出了很好的样板.
http://msdn.microsoft.com/en-us/library/ms998558.aspx
http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=486
http://www.yoda.arachsys.com/csharp/singleton.html
http://codebetter.com/blogs/steve.hebert/archive/2005/12/16/135697.aspx
另外特别说明的是:
1. Singleton class的Instance属性或者公开方法GetInstance()不应带参数, 也就是说无论在什么情况下获取Singleton类的对象, 都始终是那一个, 不会有不同的实例.
2. Singleton类不能是Static类, 因为Static类不能包含任何实例方法(包括实例constructor). 但Singleton类一定要包含一个static变量(比如_Instance, 其类型为Singleton)来存储Singleton类的实例, 那为什么必须是static变量呢? 因为我们在使用Singleton类的时候, 不是通过constructor类创建一个Singleton对象, 而是需要通过调用一个static方法(比如GetInstance())来创建一个实例, 而static方法只能使用static变量, 不能使用任何非static变量, 这就要求存放Singleton实例的内部变量必须是static变量.
3. Singleton类必须要使用lock, 否则就不是线程安全的, 除非_Instance这个static变量, 同时被声明为readonly.
4. 一般情况下, 不要Singleton模式, 不必使用Double check+Lock这样的代码, 因为写法复杂, 而且容易会造成线程不安全. 尤其是在多核机器上. 可以直接使用Single Check+Lock写法, 简单而又安全.
Bad Code
Good Code
// Bad code! Do not use! Not Thread safe, 
public sealed class Singleton
{
static Singleton instance=null;
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
//Thread safe, 不是Lazy load, 特点:实现简单
//But performance suffers as a lock is acquired every time the instance is requested
public sealed class Singleton
{
static Singleton instance = null;
static readonly object padlock = new object();

Singleton()
{
}

public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}


// Try to double Check, But still is not thread safe! Do not use!
//Thread safe issue see:
//
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();

Singleton()
{
}

public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
//Thread safe,虽然不是Lazy load. 它使用了static Initilization,特点:简单
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();

private Singleton(){}

public static Singleton Instance
{
get
{
return instance;
}
}
}
// double Check, Thread not safe
//Thread safe issue see:
//
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();

Singleton()
{
}

public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
if (instance==null)
{
//线程不安全,因为编译器会进行代码优化,
//临时变量tempInstance会被舍弃,这样就编程了线程不安全代码了
Singleton tempInstance=new Singleton();
instance = tempInstance;

}
}
}
return instance;
}
}
}
// double Check, Thread safe
public sealed class Singleton
{
volatile static Singleton instance=null;
static readonly object padlock = new object();

Singleton()
{
}

public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
//线程安全,因为instance被标记为volatile
if (instance==null)
{
Instance=new Singleton();
}
}
}
return instance;
}
}
}

thread-safe, not quite as lazy, without using locks
thread-safe, fully lazy instantiation
public sealed class Singleton
{
static readonly Singleton instance=new Singleton();

// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}

Singleton()
{
}

public static Singleton Instance
{
get
{
return instance;
}
}
}
public sealed class Singleton
{
Singleton()
{
}

public static Singleton Instance
{
get
{
return Nested.instance;
}
}

class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}

internal static readonly Singleton instance = new Singleton();
}
}

Tuesday, May 6, 2008

How to design a class which has absolute readonly List property


有时候, 我们设计的类,包含一个Collection对象, 要求该类的使用者可以访问这个Collection属性, 而不能修改Collection的Item, 比如一个容器类.
如果这个属性是List<T>类型的话, 即使该Collection属性只有getter方法, 也无法阻止容器类的使用者修改的Collection的元素.



.Net的System.Collections.ObjectModel命名空间中包含一个ReadOnlyCollection<T>,
可以帮你做到对Collection的绝对只读封装. 其实ReadOnlyCollection类实现了IList<T>接口, 但它没有将Items属性设置public, 而是将它设为protected,
这样就ReadOnlyCollection对象就不能得到Items属性, 但可以通过属性索引来查的单个元素的值.ReadOnlyCollection的这种实现方法值得好好学习.





/// <summary>
/// CtrlContainer的使用者
/// </summary>
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}


private void button1_Click(object sender, EventArgs e)
{
CtrlContainer cc = new CtrlContainer("file1.dfm");
//可以通过cc.ListControl属性增加一个元素
cc.ListControl.Add("ImageBox");

//不能可以通过cc.ReadOnlyListControl属性增加或修改一个元素, 只能读取某个元素
string firstControlName = cc.ReadOnlyListControl[0];
MessageBox.Show(firstControlName);
}
}


/// <summary>
/// 一个展现ReadOnlyCollection的Demo Class,
/// 这个类是一个Control的容器类, 它通过分析一个dfm文件, 获取该文件包含的所有Control,
/// 不允许CtrlContainer类的使用者修改Control的列表
/// </summary>
public class CtrlContainer
{
private List<string> m_ListControl = new List<string>();


/// <summary>
/// 这个只读属性其实并不能很好地防止CtrlContainer类的使用者修改m_ListControl的元素,
/// 因为他仍然可以使用ListControl.Add()等方法.
/// </summary>
public List<string> ListControl
{
get { return m_ListControl; }
}

//using System.Collections.ObjectModel;
ReadOnlyCollection<string> m_ReadOnlyListControl;


/// <summary>
/// 这个属性可以确保CtrlContainer类的使用者不能修改m_ListControl的元素, 只能读取m_ListControl的元素
/// </summary>
public ReadOnlyCollection<string> ReadOnlyListControl
{
get { return m_ReadOnlyListControl; }
}


public CtrlContainer(string dfmFileName)
{
//parse the dfm file, and extract all controls
m_ListControl.Add("Button");

// 因为m_ListControl是以引用的方式传给ReadOnlyCollection,
// 所以, 无论是在创建m_ReadOnlyListControl之前或之后, 对m_ListControl的元素进行操作, 都将反映到m_ReadOnlyListControl上
m_ReadOnlyListControl = new ReadOnlyCollection<string>(m_ListControl);

m_ListControl.Add("Richbox");
m_ListControl.Add("ComboBox");
}
}

Monday, May 5, 2008

A simple method to parse characteristic text to enum


设想下面的场景, 我们要解析一个文本文件, 在该文件中包含一些特征文本, 有richbox, 有combobox, 就像是delphi的dfm文件一样. 然后我们要针对不同的特征做不同的处理.


这时, 我们往往定义一个枚举类型, 然后读取文本, 根据特征文本, 转成一个枚举值.


我以前的做法是定义一个SortedList<string,ControlType>, 其中包含richbox和对应的枚举值. 在解析特征文本的时候, 通过这个SortedList, 就可以得到枚举值.



其实, 这个过程也可以使用Enum这个类的Parse()来完成, 前提是你定义的枚举值的名称和特征文本一摸一样(大小写可以不同). 下面是一个示例:



public class EnumMapping
{

/// <summary>
/// 根据枚举的名称,返回对应的枚举值
/// </summary>
/// <param name="enumName"></param>
/// <returns></returns>
public ControlType ConvertFromName(string enumName)
{
return (ControlType)Enum.Parse(typeof(ControlType), enumName, true);
}
}


public enum ControlType
{
richbox,
combobox,
image
}

How to handle xml namespace and xpath by using Linq Xml


.Net3.5对XML的支持更进一步, 你可以彻底地抛弃Dom处理方式了. 因为XDocument和XElement以及XNode比之前的XmlDocument和XmlNode处理速度更快.



下面是一个相对较为复杂的例子, 其中展现了怎样处理Xml的Namespace以及如何使用XPath来定位一个Xml元素.



/// <summary>
/// 查找所有节点名为p:cNvPr的Xml元素
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_SelectElementByXPath_Click(object sender, EventArgs e)
{
XDocument xDoc=XDocument.Load(@"c:\\slide1.xml");


//using System.Xml.XPath
// XElement或XDocument之所以有能力处理XPath, 是靠System.Xml.XPath.Extensions这个扩展static类提供的功能
// public static IEnumerable<XElement> XPathSelectElements(this XNode node, string expression, IXmlNamespaceResolver resolver)
// XPathSelectElements()函数还需要一个参数作为XML Namespace的解析器, 而XmlNamespaceManager类就是一个这样的解析器,
// 所以还需要引入System.Xml命名空间, 来创建一个XmlNamespaceManager对象

NameTable nt = new NameTable();
XmlNamespaceManager nameMgr = new XmlNamespaceManager(nt);
nameMgr.AddNamespace("p", "http://schemas.openxmlformats.org/presentationml/2006/main");

//***注意参数应该是p:cNvPr, 而不是cNvPr
var elements = from element in xDoc.XPathSelectElements("//p:cNvPr",nameMgr)
select element ;
foreach (var element in elements)
{
System.Console.Out.Write(element);
System.Console.Out.WriteLine("==============");
}

}


/// <summary>
/// 查找所有节点名为p:cNvPr的Xml元素
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_SelectElementByDescendant_Click(object sender, EventArgs e)
{
XDocument xDoc = XDocument.Load(@"c:\\slide1.xml");
XNamespace xns = XNamespace.Get("http://schemas.openxmlformats.org/presentationml/2006/main");

//public IEnumerable<XElement> Descendants(XName name)

//***注意参数应该是cNvPr, 而不是p:cNvPr
var elements1 = from element in xDoc.Descendants(xns.GetName("cNvPr"))
select element ;

//因为XNamespace类重载了加法运算符号,
// 所以对一个XNamespace对象和一个localName字符串相加, 返回的是一个XName对象,
// 正是 XElement或XDocument的Descendants()函数所需要的参数类型

//下面代码返回的elements1, 和elements1是完全一样的
var elements2 = from element in xDoc.Descendants(xns + "cNvPr")
select element ;

foreach(var element in elements2)
{
System.Console.Out.Write(element) ;
System.Console.Out.WriteLine("==============");
}
}