Showing posts with label UsefulCode. Show all posts
Showing posts with label UsefulCode. Show all posts

Friday, October 17, 2008

Asynchronious call Delegate

参见 http://blogs.yodersolutions.com/net/?p=17
当我们声明了Delegate 变量(比如FooDelegate), 我们可以使用下面两种方法,来触发这个Delegate,
1. FooDelegate.Invoke(), 或者直接是FooDelegate().
2. FooDelegate.BeginInvoke().
第一种调用方法是同步执行这个delegate.
而第二种方式是异步执行这个delegate, 这个delegate会在另一个线程中被执行, 当前线程将继续执行其它代码. 需要说明的是, 一定要确保这个delegate有机会调用了EndInvoke(), 否则会有资源和内存泄漏.
异步调用的一个简单示例是:
subscriber.BeginInvoke(this, new EventArgs(), Callback, subscriber);
其中第3个参数是回调函数, 它的签名是AsynCallback delegate.当subscriber被执行完后, Callback会被调用.
其中第4个参数一定要是subscriber本身,或者是subscriber本身的一个wrapper, 这样在callback函数体中, 就能访问到subscriber本身来EndInvoke.


class Program
{
static void Main(string[] args)
{
EventPublisher eventPublisher = new EventPublisher();
eventPublisher.FooEvent += new EventHandler(eventPublisher_FooEvent1);
eventPublisher.FooEvent += new EventHandler(eventPublisher_FooEvent2);
Stopwatch sw = new Stopwatch();
sw.Start();
eventPublisher.AsynFireFooEvent();
Console.WriteLine(sw.ElapsedMilliseconds.ToString());
Console.ReadKey();
}

static void eventPublisher_FooEvent1(object sender, EventArgs e)
{
Thread.Sleep(100);
}
static void eventPublisher_FooEvent2(object sender, EventArgs e)
{
Thread.Sleep(200);
}
}



class EventPublisher
{
public event EventHandler FooEvent;


/// <summary>
/// synchronized invoke the all the subscriber services.
/// The process will return until all delegates executions are over.
/// </summary>
public void FireFooEvent()
{
if (FooEvent != null)
FooEvent(this, new EventArgs());
}


/// <summary>
/// Raising events asynchronously lets the event subscribers do their processing in another thread
/// and allows the thread that raised the event to continue processing.
/// </summary>
public void AsynFireFooEvent()
{
if (FooEvent != null)
{
Delegate[] subscribers = FooEvent.GetInvocationList();
foreach (EventHandler subscriber in subscribers)
{
//the 3rd arg should be a AsyncCallback type delegate. When one subscriber service will completed, the AsyncCallback method will be raised.

//the 4th arg should be the exact subscriber or subscriber wrapper, so that in callback function,
// we can access the subcriber to call EndInvoke()
subscriber.BeginInvoke(this, new EventArgs(), Callback, subscriber);
}
}
}



void Callback(IAsyncResult ar)
{
EventHandler raisedEvent = (EventHandler)ar.AsyncState;
raisedEvent.EndInvoke(ar);
}
}

Saturday, September 27, 2008

Using Ninject(one DI container)

在设计中, 经常会遇到这样的情况, 在一个类中要创建或使用另一个类的实例, 暂且将前一个类称为大类, 后一个类称为小类, 也许小类有很多变种, 我们会根据不同的情况, 让大类使用不同的小类变种. 可以看出这里有一个耦合关系, 一个好的办法是使用依赖注入模式(Dependency Inject), Martin Fowler 有个非常精彩的文章专门讲述DI).
DI原理其实很简单, 我们设计的大类要依赖于小类的接口或基类, 而不是一个具体的小类, 同时不要在大类中创建小类实例, 而是通过大类的构造子或property将小类的实例传进来即可.
目前.Net领域有不少DI容器框架, (这里是DI容器框架列表), 这些框架主要有两个功能, (1)帮助完成依赖的注册与依赖实例的创建, 即绑定小类接口和某个具体小类, 绑定的地方不是在大类中, 所以实现了解耦. (2)更高级的功能是, DI 容器框架可以帮助我们创建大类实例, 就像是一个Factory工厂方法一样, 当然大类所依赖的小类, 也不同我们手动创建, 容器自动按照我们定义的小类绑定关系来创建小类实例.
Ninject是一个新的DI框架, 它的特点是:
(1)不需要使用Xml来定义绑定关系, 使用一个自定义的Module类(基类为StandardModule)来定义绑定关系;
(2)不仅仅支持constructor注入, 而且支持property注册;
(3)对于constrctor注入, 构造子还可以包含其他类型参数, 不仅仅是所依赖的类型参数.
(4)对于非constructor注入, 不仅仅支持无参构造子, 而且支持带参构造子.
(5)支持基于类型的条件绑定, 可以使用Bind().To().ForMembersOf();
(6)部分支持基于variable的Context的条件绑定, 示例代码中对此有说明.(Ninject支持另一种条件绑定, 但它需要在大类的代码加上Parameter attribute或Method attribute, 增加了大类和小类的耦合度, 所以我不喜欢, 好在基于context variable的绑定基本够用了)
(7)对于利用DI容器创建的对象, 可以在定义绑定时, 指定对象的生命周期, 比如Singleton以及SingleCall的生命周期模式.

下面是一个使用Ninject的简单示例, 需要添加Ninject.core和Ninject.condition两个assembly.
主程序类和自定义的Module类:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using Ninject.Core;
using Ninject.Conditions;
using Ninject.Core.Behavior;
using Ninject.Core.Parameters;
using Ninject.Core.Activation;

namespace NinjectDemo
{
class Program
{
static void Main(string[] args)
{
#region DI: Old way
IEditor editor = new NotePad();
var csProgrammer = new WindowsProgrammer(editor);
csProgrammer.WriteCode();
#endregion

#region DI container
#region Initialization for using Ninject
var module = new ProgrammerModule();
IKernel kernel = new StandardKernel(module);
#endregion

#region Simple inject
var vbProgrammer = kernel.Get<WindowsProgrammer>();
vbProgrammer.WriteCode();

//Get correct concrete class according to binding condition
var linuxProgrammer = kernel.Get<LinuxProgrammer>();
linuxProgrammer.WriteCode();
#endregion

#region Including other parameters rather than inject interface
Dictionary<string, object> param = new Dictionary<string, object>();
//there is another parameter named firstName in constructor
param.Add("firstName", "Harry-Mac OS");
var macProgrammer = kernel.Get<MacProgrammer>(With.Parameters.ConstructorArguments(param));
macProgrammer.WriteCode();
#endregion

#region Control binding by context variable
IParameterCollection pc = With.Parameters.ContextVariable("OS", "Mac").ConstructorArguments(param);
var programmer = kernel.Get<Programmer>(pc);
macProgrammer.WriteCode();
#endregion

#endregion DI container

Console.WriteLine("Enter any key to exit...");
Console.ReadKey();
}
}


class ProgrammerModule : StandardModule
{
/// <summary>
/// 注册接口和对象的绑定
/// </summary>
public override void Load()
{
//This is a default bind for IEditor inteface
Bind<IEditor>().To<NotePad>();
//Bind<IEditor>().To<NotePad>().Always();// same to the above line

//This is a condtional bind for IEditor injection
Bind<IEditor>().To<Vi>().ForMembersOf<LinuxProgrammer>();
//or the following code
//Bind<IEditor>().To<Vi>().OnlyIf(c => c.Member.DeclaringType == typeof(LinuxProgrammer));
//or the following code
//Bind<IEditor>().To<Vi>().OnlyIf(delegate(IContext c) { return c.Member.DeclaringType == typeof(LinuxProgrammer); });

Bind<IEditor>().To<Vi>().ForMembersOf<MacProgrammer>();

//This is another kind of conditional bind.
//我们获取对象要用kernel.Get<T1>()方法, 绑定用Bind<T2>()方法,
// 这种context绑定仅仅适合于T1和T2同一类型或基于同一类型
Bind<Programmer>().To<MacProgrammer>().Only(When.Context.Variable("OS") == "Mac");
}

/// <summary>
/// Load2 方法在本项目中其实没用, 只是为了列出Ninject在绑定时可指定对象的生命周期
/// </summary>
public void Load2()
{
//每次调用kernel.Get<>(), 总是返回一个新的对象, 如果Remoting中讲的SingleCall对象
Bind<IEditor>().To<NotePad>();
//Bind<IEditor>().To<NotePad>().Using<TransientBehavior>(); //same as the above

//获取的对象是Singleton对象, 即使是不同线程, 获得的对象总是同一个
Bind<IEditor>().To<NotePad>().Using<SingletonBehavior>();

//One instance of the type will be created per web request, and will be destroyed when the request ends.
Bind<IEditor>().To<NotePad>().Using<OnePerRequestBehavior>();

//在同一个线程中, 每次调用kernel.Get<>()获取的对象总是同一个
Bind<IEditor>().To<NotePad>().Using<OnePerThreadBehavior>();
}
}

}


下面是IEditor接口和实现类:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NinjectDemo
{

interface IEditor
{
string Code();
}

public class Vi : IEditor
{
public string Code()
{
return "This is Vi editor";
}
}

public class NotePad : IEditor
{
public string Code()
{
return "This is NotePad editor";
}
}
}

下面是Programmer基类和派生类:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

#region Ninject namespace
using Ninject.Conditions;
using Ninject.Core.Behavior;
using Ninject.Core;
#endregion

namespace NinjectDemo
{
abstract class Programmer
{
IEditor m_Editor;
public Programmer(IEditor editor)
{
this.m_Editor = editor;
}

public virtual void WriteCode()
{
Console.WriteLine(m_Editor.Code());
}

}

class WindowsProgrammer:Programmer
{
public WindowsProgrammer(IEditor editor)
: base(editor)
{
}
}

class MacProgrammer : Programmer
{
string m_FirstName;

[Inject]
public MacProgrammer(IEditor editor, string firstName)
: base(editor)
{
m_FirstName = firstName;
}

public override void WriteCode()
{
Console.Write( m_FirstName+" , ");
base.WriteCode();
}
}

class LinuxProgrammer : Programmer
{
[Inject]
public LinuxProgrammer(IEditor editor)
: base(editor)
{
}
}
}

Monday, May 26, 2008

C_Sharp edition StringList Class

Delphi有一个常用的类StringList, 它有一个特点是你可以一行一行加String, 如果你所加的每行String都是Name=Value格式的,它会帮你提取出Name列表和Value列表, 这个类我在用Delphi时候, 常使用它. 下面是C#版的StringList类.



namespace LiuHarry.Utils.Foundation
{
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

public class DelphiStrList
{
private List<string> M_Items = new List<string>();
private List<string> M_Keys = new List<string>();
private string M_KeyValueEqualmark = "=";
private List<string> M_Values = new List<string>();

public virtual void Add(string item)
{
string str;
string str2;
this.M_Items.Add(item);
DivideKeyValue(item, this.M_KeyValueEqualmark, out str2, out str);
this.M_Keys.Add(str2);
this.M_Values.Add(str);
}

public virtual void Clear()
{
this.M_Items.Clear();
this.M_Keys.Clear();
this.M_Values.Clear();
}

protected void DevideKeyValuesByItems()
{
this.M_Keys.Clear();
this.M_Values.Clear();
foreach (string str3 in this.M_Items)
{
string str;
string str2;
DivideKeyValue(str3, this.M_KeyValueEqualmark, out str2, out str);
this.M_Keys.Add(str2);
this.M_Values.Add(str);
}
}

public static void DivideKeyValue(string entireStr, string keyValueEqualmark, out string keyStr, out string valueStr)
{
int index = entireStr.IndexOf(keyValueEqualmark);
if (index > 0)
{
keyStr = entireStr.Substring(0, index);
if (index < (entireStr.Length - 1))
{
valueStr = entireStr.Substring(index + 1);
}
else
{
valueStr = "";
}
}
else
{
keyStr = entireStr;
valueStr = "";
}
}

public virtual int IndexOf(string item)
{
return this.M_Items.IndexOf(item);
}

public virtual void Insert(int index, string item)
{
string str;
string str2;
this.M_Items.Insert(index, item);
DivideKeyValue(item, this.M_KeyValueEqualmark, out str2, out str);
this.M_Keys.Insert(index, str2);
this.M_Values.Insert(index, str);
}

public string MakeItemsToText(string ItemSeperator)
{
return this.MakeStrlistToText(this.M_Items, ItemSeperator);
}

public string MakeKeyItemsToText(string ItemSeperator)
{
return this.MakeStrlistToText(this.M_Keys, ItemSeperator);
}

private string MakeStrlistToText(List<string> items, string ItemSeperator)
{
StringBuilder builder = new StringBuilder();
foreach (string str in items)
{
builder.Append(str);
builder.Append(ItemSeperator);
}
return builder.ToString();
}

public string MakeValueItemsToText(string ItemSeperator)
{
return this.MakeStrlistToText(this.M_Keys, ItemSeperator);
}

public virtual bool Remove(string item)
{
int index = this.IndexOf(item);
if (index >= 0)
{
this.M_Keys.RemoveAt(index);
this.M_Values.RemoveAt(index);
}
return this.M_Items.Remove(item);
}

public virtual void RemoveAt(int index)
{
this.M_Items.RemoveAt(index);
this.M_Keys.RemoveAt(index);
this.M_Values.RemoveAt(index);
}

public virtual void Sort()
{
this.M_Items.Sort();
this.DevideKeyValuesByItems();
}

public int Count
{
get
{
return this.M_Items.Count;
}
}

public List<string> Items
{
get
{
return this.M_Items;
}
}

public List<string> Keys
{
get
{
return this.M_Keys;
}
}

public string KeyValueEqualmark
{
get
{
return this.M_KeyValueEqualmark;
}
set
{
this.M_KeyValueEqualmark = value;
}
}

public List<string> Values
{
get
{
return this.M_Values;
}
}
}
}

How to hide or show TabPage of TabControl

C# 的TabControl功能严重不足, 尤其是你用它来制作Wizard界面时候, 下面代码会有所帮助的.


namespace LiuHarry.Utils.Components
{
using System;
using System.Windows.Forms;

public class TabControlHelper
{
private TabControl m_tabControl;

public TabControlHelper(TabControl tabCtrl)
{
this.m_tabControl = tabCtrl;
}

public void HideTabPage(TabPage tp)
{
if (this.m_tabControl.TabPages.Contains(tp))
{
this.m_tabControl.TabPages.Remove(tp);
}
}

private void InsertTabPage(TabPage tabpage, int index)
{
if ((index < 0) || (index > this.m_tabControl.TabCount))
{
throw new ArgumentException("Index out of Range.");
}
this.m_tabControl.TabPages.Add(tabpage);
if (index < (this.m_tabControl.TabCount - 1))
{
do
{
this.SwapTabPages(tabpage, this.m_tabControl.TabPages[this.m_tabControl.TabPages.IndexOf(tabpage) - 1]);
}
while (this.m_tabControl.TabPages.IndexOf(tabpage) != index);
}
this.m_tabControl.SelectedTab = tabpage;
}

public void ShowTabPage(TabPage tp)
{
this.ShowTabPage(tp, this.m_tabControl.TabPages.Count);
}

public void ShowTabPage(TabPage tp, int index)
{
if (!this.m_tabControl.TabPages.Contains(tp))
{
this.InsertTabPage(tp, index);
}
}

private void SwapTabPages(TabPage tp1, TabPage tp2)
{
if (!(this.m_tabControl.TabPages.Contains(tp1) && this.m_tabControl.TabPages.Contains(tp2)))
{
throw new ArgumentException("TabPages must be in the TabControls TabPageCollection.");
}
int index = this.m_tabControl.TabPages.IndexOf(tp1);
int num2 = this.m_tabControl.TabPages.IndexOf(tp2);
this.m_tabControl.TabPages[index] = tp2;
this.m_tabControl.TabPages[num2] = tp1;
this.m_tabControl.SelectedIndex = this.m_tabControl.SelectedIndex;
string text = tp1.Text;
string str2 = tp2.Text;
tp1.Text = str2;
tp2.Text = text;
}
}
}

DataGridViewHelper class

DataGridView这个控件在开发中经常会被用到, C#的组件总觉得不如Delphi组件那么容易使用, 怎样在Grid上选择一个Row, 怎样选择一个Cell, 我就被block了好久. 下面是代码:

namespace LiuHarry.Utils.Components
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;

public class DataGridViewHelper
{
private DataGridView m_DataGridView;
private SortedList<string, string> m_MapListFieldColumn = new SortedList<string, string>();
private DataView m_ShowedDataViewInGrid;

public DataGridViewHelper(DataGridView dataGridView, DataView showedDataViewInGrid)
{
this.m_DataGridView = dataGridView;
this.m_ShowedDataViewInGrid = showedDataViewInGrid;
this.InitFieldColumnMapList();
}

private string _GetGridViewColumnName(string dataFieldName)
{
for (int i = 0; i < this.m_DataGridView.Columns.Count; i++)
{
if (this.m_DataGridView.Columns[i].DataPropertyName == dataFieldName)
{
return this.m_DataGridView.Columns[i].Name;
}
}
return "";
}

private void InitFieldColumnMapList()
{
this.m_MapListFieldColumn.Clear();
for (int i = 0; i < this.m_ShowedDataViewInGrid.Table.Columns.Count; i++)
{
string columnName = this.m_ShowedDataViewInGrid.Table.Columns[i].ColumnName;
string str2 = this._GetGridViewColumnName(columnName);
this.m_MapListFieldColumn.Add(columnName, str2);
}
}

public string QuickFindGridViewColumnName(string dataFieldName)
{
int num = this.m_MapListFieldColumn.IndexOfKey(dataFieldName);
if (num < 0)
{
throw new Exception("There no a column in DataGridView corresponding DataFieldName=" + dataFieldName);
}
return this.m_MapListFieldColumn.Values[num];
}

public void SelectAndScrollToCell(string fieldName, object fieldValue)
{
this.m_DataGridView.ClearSelection();
if ((fieldName != null) && (fieldValue != null))
{
string str = this.QuickFindGridViewColumnName(fieldName);
foreach (DataGridViewRow row in (IEnumerable) this.m_DataGridView.Rows)
{
if (object.Equals(row.Cells[str].Value, fieldValue))
{
this.m_DataGridView.CurrentCell = row.Cells[str];
this.m_DataGridView.CurrentCell.Selected = true;
break;
}
}
}
}

public void SelectRow(object valueOfSortedField)
{
DataView showedDataViewInGrid = this.m_ShowedDataViewInGrid;
this.m_DataGridView.ClearSelection();
if ((showedDataViewInGrid != null) && (valueOfSortedField != null))
{
int num = showedDataViewInGrid.Find(valueOfSortedField);
if (num >= 0)
{
this.m_DataGridView.Rows[num].Selected = true;
}
}
}

public SortedList<string, string> MapListFieldColumn
{
get
{
return this.m_MapListFieldColumn;
}
}
}
}

SQL Execute Trace Helper Class

http://focuswindows.blogspot.com/2008/01/sql-executing-monitor-code.html
该版本可能不是最新的, 最新的代码应该是下面:



namespace LiuHarry.Utils.DB
{
using LiuHarry.Utils.Foundation;
using System;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;

internal class SqlExecuteTracer : IDisposable
{
private bool disposed = false;
private string m_ErrorLogFile;
private readonly string m_ErrorLogFileSuffix = "SqlError.txt";
private bool m_LogSqlExecute = false;
private string m_SqlLogFile;
private readonly string m_SqlLogFileSuffix = "SqlLog.txt";
private TextWriterTraceListener m_SqlLogListener;

public SqlExecuteTracer()
{
string assemblyFileWithoutExt = this.GetAssemblyFileWithoutExt();
this.m_ErrorLogFile = assemblyFileWithoutExt + "_" + this.m_ErrorLogFileSuffix;
this.m_SqlLogFile = assemblyFileWithoutExt + "_" + this.m_SqlLogFileSuffix;
}

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

private void Dispose(bool disposing)
{
if (!this.disposed && this.m_LogSqlExecute)
{
this.m_SqlLogListener.Close();
}
this.disposed = true;
}

private string GetAssemblyFileWithoutExt()
{
string location = Assembly.GetExecutingAssembly().Location;
return (Path.GetDirectoryName(location) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(location));
}

private string GetSqlFromCommand(IDbCommand cmd)
{
string commandText = cmd.CommandText;
if (cmd.Parameters.Count <= 0)
{
return commandText;
}
DelphiStrList list = new DelphiStrList();
foreach (IDbDataParameter parameter in cmd.Parameters)
{
if (parameter.Value != null)
{
list.Add(parameter.ParameterName + "=" + parameter.Value.ToString());
}
else
{
list.Add(parameter.ParameterName + "=NULL");
}
}
return (commandText + Environment.NewLine + list.MakeItemsToText(Environment.NewLine));
}

public void TraceErrorSqlToLog(IDbCommand cmd, string ErrorMessage)
{
using (TraceListener listener = new TextWriterTraceListener(this.m_ErrorLogFile))
{
string sqlFromCommand = this.GetSqlFromCommand(cmd);
this.TraceSqlToLogListener(sqlFromCommand, ErrorMessage, listener);
listener.Close();
}
}

public void TraceErrorSqlToLog(string sql, string ErrorMessage)
{
using (TraceListener listener = new TextWriterTraceListener(this.m_ErrorLogFile))
{
this.TraceSqlToLogListener(sql, ErrorMessage, listener);
listener.Close();
}
}

public void TraceSqlToLog(IDbCommand cmd)
{
if (this.m_LogSqlExecute)
{
string sqlFromCommand = this.GetSqlFromCommand(cmd);
this.TraceSqlToLogListener(sqlFromCommand, null, this.m_SqlLogListener);
}
}

public void TraceSqlToLog(string sql)
{
if (this.m_LogSqlExecute)
{
this.TraceSqlToLogListener(sql, null, this.m_SqlLogListener);
}
}

private void TraceSqlToLogListener(string sql, string ErrorInfo, TraceListener listener)
{
listener.WriteLine("===================================");
listener.WriteLine(DateTime.Now.ToString());
if (!string.IsNullOrEmpty(ErrorInfo))
{
listener.WriteLine("Error Info:");
listener.WriteLine(ErrorInfo);
listener.WriteLine("SQL:");
}
listener.WriteLine(sql);
listener.WriteLine("===================================");
listener.WriteLine("");
listener.Flush();
}

public bool LogSqlExecute
{
get
{
return this.m_LogSqlExecute;
}
set
{
this.m_LogSqlExecute = value;
if (this.m_LogSqlExecute && (this.m_SqlLogListener == null))
{
this.m_SqlLogListener = new TextWriterTraceListener(this.m_SqlLogFile);
}
}
}
}
}

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");
}

}


}

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");
}
}