Saturday, September 19, 2009

VS2008 SP1 ISO version

VS2008 RTM的版本是9.0.21022, 打sp1之后为9.0.3

VS2008 SP1在线安装包vs90sp1-KB945140大小为500K多, 打补丁的过程会非常慢的, 因为下载后总共有800M多. 为了加速打补丁的过程, 可以在MS网站下载SP1 的ISO版本, 参考下面两个博客:

http://blog.miniasp.com/post/2008/08/Microsoft-Visual-Studio-2008-Service-Pack-1-Released.aspx
http://www.cnblogs.com/vainnetwork/archive/2008/08/13/1266982.html

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

Thursday, October 9, 2008

Auto build script by using NAnt

当我们想专门为一个C#项目架一个build服务器, 怎样对VS的solution文件进行自动build是个问题, 最简单的办法是, 编写一个bat文件, 运行这个bat文件就能完成solution的编译甚至运行单元测试 . NAnt是个很好的选择.
简单地讲, NAnt就是一个能将xml文件作为参数的命令行工具, 你可以在这个xml中按照NAnt的schema编写你想要完成的任务, 比如压缩文件, 比如删除目录, 当然NAnt最主要的作用是可以编写任务来批量编译c#的代码. 但是考虑到MS官方已经发布了msbuild, 所以一个比较好的做法是:在NAnt的参数xml文件中, 使用msbuild来编译.Net solution, 用NAnt本身的功能完成其它事情.
当然对于Nant新手来说, 编写这样的xml并不简单. 幸好我们有一些现成工具可以使用, Tree Surgeon就是这样一个开源项目(http://www.codeplex.com/treesurgeon).
TreeSurgeon不仅仅帮你编写xml的build文件(你可以根据需要做比较的修改), 而且会自动将NAnt以及NUnit等整理到一个tools目录. Treesurgeon创建的solution目录结构也很有参考价值, 比如和src目录同级别的目录, 还有bin, tools, lib, dist目录, 其中bin是存放solution的输出文件(需要在Visual studio中将这个solution的所有project的output目录制定到这个bin目录), dist目录是专门作为solution的release目录.
如果想测试这个xml文件中某些变量设置是否正确, 可以使用在project tag之下, 创建echo tag, 在用NAnt执行这个xml文件时, echo中的message信息就会被输出到屏幕上.


<echo message="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe"/>

下面的bat和xml是使用NAnt的示例(它是由TreeSurgeon生成, 并参考了Ninject源码中的NAnt build_xml文件). 使用非常方便,
cmd> build 或
cmd> build test
build.bat的内容是:


@tools\nant\NAnt.exe -buildfile:DbGear.build %*
rem pause

DbGear.build是一个xml文件, 用NAnt命令行程序执行这个xml文件(NAnt0.86版本如用msbuild tag来调用.net 3.5 的msbuild时候, 会有bug. 所以这个例子并没有使用常规的msbuild tag,而是使用 exec tag直接调用3.5版本的msbuild命令行程序)


<?xml version="1.0" ?>
<!--refer to treesurgeon on codeplex, and build file of ninject project-->
<project name="DbGear" default="compile" xmlns="http://nant.sf.net/schemas/nant.xsd">
<!--tell NAnt try to use msbuild from .net sdk 3.5-->
<property name="nant.settings.currentframework" value="net-3.5" />

<!--declare path variables-->
<property name="path.base" value="${project::get-base-directory()}"/>
<property name="path.src" value="${path.base}/src"/>
<property name="path.tools" value="${path.base}/tools"/>
<property name="path.build" value="${path.base}/bin"/>
<property name="path.dist" value="${path.base}/dist"/>
<property name="path.build.testresult" value="${path.build}/test-result"/>
<property name="path.build.debug" value="${path.build}/debug"/>

<!--define solution variables-->
<property name="version" value="1.0.0.0" overwrite="false"/>
<property name="file.solution" value="${path.src}/DbGear.sln"/>
<!--choose one suitable compiling config, eg Debug, Release or other customized config schema-->
<property name="build.configuration" value="debug"/>

<!-- define build targets -->
<target name="clean" description="Delete Automated Build artifacts">
<delete dir="${path.build}" if="${directory::exists(path.build)}"/>
<delete dir="${path.dist}" if="${directory::exists(path.dist)}"/>
</target>

<target name="init" description="create assembly out path">
<mkdir dir="${path.dist}" if="${not(directory::exists(path.dist))}"/>
<mkdir dir="${path.build}" if="${not(directory::exists(path.build))}"/>
<mkdir dir="${path.build.testresult}" if="${not(directory::exists(path.build.testresult))}"/>
<mkdir dir="${path.build.debug}" if="${not(directory::exists(path.build.debug))}"/>
</target>



<echo message="Build configuration:" />
<echo message="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe"/>

<!--nant0.86 has a bug with Framework 3.5 section, so we will use exec to call msbuild.exe of 3.5, rather than the regular msbuild tag -->
<!--
<target name="compile" depends="init" description="Compiles using the AutomatedDebug Configuration">
<msbuild project="${file.solution}">
<property name="Configuration" value="${build.configuration}" />
</msbuild>
</target>
-->

<target name="compile" depends="init" description="Compiles using the Debug Configuration">
<exec program="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe" >
<arg value="${file.solution}" />
<arg value="/verbosity:minimal" />
<arg value="/property:Configuration=${build.configuration}" />
<arg value="/property:WarningLevel=0" />
</exec>
</target>

<target name="test" depends="compile, unittest"
description="Compile and Run Tests" />

<target name="full" depends="clean, test"
description="Compiles, tests, and produces distributions" />

<!-- Internal targets -->
<target name="unittest" description="runs the unit tests">
<!-- Test Assembly -->
<exec program="${path.tools}/nunit/nunit-console.exe">
<arg value="${path.build.debug}/UnitTests.dll" />
<arg value="/xml=${path.build.testresult}/UnitTests-Results.xml" />
</exec>
</target>


<target name="dist">
<zip zipfile="${path.dist}\DbGear-${version}.zip">
<fileset basedir="${path.build.debug}">
<include name="**/*" />
<exclude name="**/*.pdb" />
</fileset>
</zip>
</target>


<target name="package-source">
<zip zipfile="${path.dist}\DbGear-${version}-source.zip" ziplevel="9">
<fileset basedir="${path.src}">
<include name="**/*.cs"/>
<include name="**/*.csproj"/>
<include name="**/*.sln"/>
<include name="**/*.txt"/>
<include name="**/*.build"/>
<exclude name="**/*.pdb" />
</fileset>
</zip>
</target>
</project>

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)
{
}
}
}

Tuesday, August 26, 2008

check exchange mail by firefox

This summary is not available. Please click here to view the post.

Thursday, August 21, 2008

svn authorization

预先的环境是: svn server的IP是: 11.12.13.14, OS是windows 2003 server. repository的目录是D:\source, 我们在该repository下建立了两个项目, 分别是Project1和Project2. 建立repository的过程, 见这个blog
常用的Svn访问协议有三种, 第一种是file协议, 第二是svn协议, 第三是https协议.
https协议比较适合于跨广域网的项目开发, 需要有apache的支持, 所以配置比较复杂一些, 可参考网络上的文章 .
  1. Setting up an ASP.NET website development environment using Visual Studio .NET, Subversion, and Windows XP http://www.codeproject.com/KB/aspnet/Subversion.aspx
  2. HOWTO: Subversion for Windows with Apache server - a beginner's guide [version 0.4] http://svn.spears.at/
  3. Setting Up Subversion for One or Multiple Projects http://www.linuxjournal.com/article/7655
file协议最适合一个人的项目, 设置非常简单, 你在你本机开一个svn server, 同时在你本机创建一个repository. 你的svn客户端就可以用file协议访问repository了. 也可以多人一起用, 但必须将repository 的目录共享出来, 所以安全性很差, 不建议在多人项目中使用这种模式.
本机file协议访问project1的url是 file:///D:/source/project1 .
如果是局域网访问的话是, file:///\11.12.13.14/source/project1
Svn协议比较适合于局域网内部多人项目的开发, 在svn server建立了一个repository之后, 我们就可以使用svn协议了, URL是 svn://11.12.13.14/source/project1 ,这是因为缺省情况下, 这个repository已经被开启了匿名读权限了, 但写权限被关闭了. 所以任何人都可以读取这个repository的内容. 我们需要为每个人定制不同的访问权限. 需要修改这个repository目录下的三个文件, 它们都在D:\harry\source\conf下.
(1) svnserve.conf文件, 你需要处理掉4个label的注释, 分别是authz-db和password-db和anon-access和auth-access.需要注意的是, 确保每行前边没有多余的空格, 否则当你访问svn会报错. 另外最好为realm这个label设置一个有意义的名称, 因为svn client连接server时候, 会显示server的realm名称. 同时如果你想让多个repository共用同一组用户账号, 可以将这几个repository的passwd文件指向同一个文件, 可以使用相对路径来指定passwd文件(相对路径是基准是conf目录), 也可以用绝对路径来指定. 需要注意的, 路径必须使用unix风格的分隔符/, 而不能是\.

(2) passwd文件, 在[users]section下定义用户和密码, 每个账号一行,用户名和密码用=号分隔 .
(3) authz文件, 这是最关键的配置文件. 下面是authz文件的配置:
[groups]
Prj1_Dev_Team=harry,helen
Prj2_Dev_Team=tom,peter
Test_Team=sally,mark
QA_Team=andy

[/]
*=r #所有人都能读取这个repository
harry=rw #harry具有这个repository的读写权限

[/project1]
#*=rw #取消所有人的读写权限
@Prj1_Dev_Team=rw
@SE_Team_QA=rw

[/project2]
#*=rw
@Prj2_Dev_Team=rw
@SE_Team_QA=rw
@Test_Team=rw

Saturday, August 16, 2008

Registry requisite tools

Regsnap -registry and windows system folder snapshot tool and analysis tool.
RegMon.exe--product from sysinternal company, it is free tool