[Erlang]进入Erlang的世界

mikel阅读(1056)

原创作者: litaocheng  

进入Erlang的世界

作为程序员,我们曾经闻听很多“业界动态”,“技术革新”,曾经接触很多“高手箴言”,“权威推荐”。这些正确与否,都已成过去!

现在,让我们迎接Erlang盛宴!

经历

200711月在koders.com搜索代码时,发现*.erl格式的源文件,感叹开发语言的花样百出,此时,我觉得erlang是一个丑陋的小家伙,看名字就没有对它提起多少兴趣。

2008年初的时候,公司的项目开发中,我有缘认识了ejabberd,一个采用Erlang开发的开源jabber服务器。我开始为其诱人的特性所倾倒。是时候认真看看Erlang到底什么样了!

20084月,通过各种资料的搜集,了解,我决定系统的学习Erlang

今天,通过4个月的认真学习,我已经熟悉了Erlang,已经在使用Erlang开发项目。作为C++程序员,我不敢妄自使用“熟悉”,“精通”之类的字眼,但是对于Erlang我可以很负责任的说,Erlang很巧,很强大!

困惑

面对一个新的事务,我们本性都会充满好奇,可是作为程序员,很多时候对于新的语言我都充满了抵触:这个新东西值得学习么?它会不会让我抛弃我的旧爱?它文档丰富么?其是不是很难理解?它的前景如何?…等等,相信大家跟我有一样的苦恼。

但是,请听我说!我们是程序员,我们走在技术革新的最前沿。用户的产品,体验是通过我们来产生!我们不能畏缩不前,我们的固步自封,就是我们的公司,乃至整个我们的行业的停滞不前!口号可能有些响亮,但是认真思考,我相信朋友们一定有所感悟。

Erlang是什么

Erlang是什么是我们最先要面对的问题,只有清楚了它是什么,我们才能做出我们的决定。可见这个问题的重要性,它决定了很多读者是否会继续看下去!非常紧张。

Erlang最初是爱立信为开发电信相关产品而产生。

Erlang是一种面向并发(Concurrency oriented),面向消息(Message oriented)的函数式(Functional)编程语言。

面向并发说明Erlang支持大规模的并发应用,我们可以在应用中处理成千上万的并发,而不相互影响。面向消息,其实是为并发服务!我们应该都熟悉多线程,熟悉加锁解锁操作,熟悉可能出现的资源竞赛与死锁。在Erlang的世界里,我们可以将轻轻的抹去这些令人苦恼的词汇。Erlang的世界,每个处理都是独立的个体,他们之间的交互仅仅靠消息!因此不会有死锁,不会有痛苦的编程经历。

Erlang中一个非常重要的名词:Process,也就是我们前面提到的“个体”。它不是我们操作系统中的进程,也不是线程。它是Erlang提供给我们的超级轻量的进程。为了适应大规模并发的特性,Process需要能够快速创建,快速销毁。Process之间通信的唯一方法就是消息,我们只要知道一个Process的名字即pid,就可以向其发送消息。Process也可以在任何时候,接收消息。我们这样做只有一个目的:让我们的系统更加简单,用一种朴素的做法,实现一个高效的语言。

Erlang是种函数式编程语言,对此我没有很深刻的理解,最明显的特征就是,Erlang中到处都是函数,函数构成了我们的产品的主体,把这些函数放到一个个的Process中去,让他们运行起来,那么就组成了我们朝气蓬勃的产品。

Erlang支持对数据的位操作,拥有丰富的数据持久化机制。

同时需要说明的是Erlang内建垃圾回收机制(GC)。

Erlang语言特性

简单小巧

Erlang中只有8种基本的数据类型:

Integerfloatatomreferencefunportpidbitstring

同时提供2种复合结构:tuplelist,这就是Erlang的所有数据类型。

模式匹配

Erlang的函数中,某些语法中,我们可以使用Pattern匹配,这是一个非常好的特性,我们可以让代码自己去决定如何执行

比如,我们定义一个函数,其告诉我们某种水果的价格:

price(apple) -> 2.0;

price(banana) -> 1.2.

我们随后调用 price(Fruit),会根据Fruit变量的内容返回具体的价格。这样做的好处就是节省了我们的代码量,我们不用if…else…或者switch…case的来伺候了。也便于代码的扩展:加一个新的水果品种,我们只需要加一行就可以了。

学习Erlang一个非常重要的内容就是模式匹配,但是请不要混淆,这个匹配和正则表达式没有任何干系。

变量单次赋值

这个是一个匪夷所思的特性,变量竟然只能单次赋值!是的Erlang中变量一旦绑定某个数值以后,就不能再次绑定,这样做的好处是便于调试出错。当发生错误时,某个变量是什么就永远是什么,不用顺藤摸瓜的查找谁修改过它,省了好多事情。唯一的麻烦就是需要一个信的变量时,你必须再为它想一个名字。

丰富的libs

Erlang中提供丰富的libs

stdlib中包含大量的数据结构如listsarraydictgb_setsgb_treesetsdets

mnesia提供一个分布式的数据库系统

inets提供ftp clienthttp client/servertftp client/server

crypto 提供加密解密相关函数,基于openssl相关实现

ssl 实现加密socket通信,基于openssl实现

ssh 实现ssh协议

xmerl 实现XML相关解析

snmp 实现SNMP协议(Simple Network Management Protocol

observer 用来分析与追踪分布式应用

odbc 使Erlang可以连接基于SQL的数据库

orber 实现CORBA对象请求代理服务

os_mon 提供对操作系统的监控功能

dialyzer提供一个静态的代码或程序分析工具

edoc 依据源文件生成文档

gs 可以为我们提供某些GUI的功能(基于Tcl/Tk

还有很多朋友提供了一些开源的lib,比如eunit,用来进行单元测试。

灵活多样的错误处理

Erlang最初为电信产品的开发,这样的目的,决定了其对错误处理的严格要求。Erlang中提供一般语言所提供的exceptioncatchtry…catch等语法,同时Erlang支持LinkMonitor两种机制,我们可以将Process连接起来,让他们组成一个整体,某个Process出错,或推出时,其他Process都具有得知其推出的能力。而Monitor顾名思义,可以用来监控某个Process,判断其是否退出或出错。所有的这些Erlang都提供内在支持,我们快速的开发坚固的产品,不在是奢望。

代码热替换

你的产品想不间断的更新么?Erlang可以满足你这个需求,Erlang会在运行时自动将旧的模块进行替换。一切都静悄悄。

天生的分布式

Erlang天生适合分布式应用开发,其很多的BIF(内建函数,相API)都具有分布式版本,我们可以通过BIF在远程机器上创建Process,可以向远程机器上的某个Process发送消息。在分布式应用的开发中,我们可以像CC++,JAVA等语言一样,通过Socket进行通讯,也可以使用Erlang内嵌的基于Cookie的分布式架构,进行开发。当然也可以两者混合。分布式开发更加方便,快速。ErlangProcess的操作,Error的处理等都对支持分布式操作。

超强的并发性

由于采用其自身Process,而没有采用操作系统的进程和线程,我们可以创建大规模的并发处理,同时还简化了我们的编程复杂度。我们可以通过几十行代码实现一个并发的TCP服务器,这在其他语言中都想都不敢想!

多核支持

Erlang让您的应用支持多个处理器,您不需要为不同的硬件系统做不同的开发。采用Erlang将最大限度的发挥你的机器性能。

跨平台

如同JAVA一样,Erlang支持跨平台(其目前支持linuxmacwindows19种平台),不用为代码的移植而头疼。

我们仅仅需要了解平台的一些特性,对运行时进行优化。

开源

开源是我非常喜欢的一个词汇,开源意味这更加强壮,更加公开,更加的追求平等。开源会让Erlang更好。

Erlang与外界的交互

Erlang可以与其他的语言进行交互,如CC++,Java。当然也有热心的朋友提供了与其他语言的交互,如果需要你也可以根据Erlang的数据格式,提供一个库,让Erang与您心爱的语言交互。

Erlang支持分布式开发,您可以创建一个C Node,其如同一个Erlang节点,前提是你遵照Erlang的规范。

当然最常用的交互还是再同一个Node上,比如我们要调用某个lib,调用一些系统提供的功能,这时候主要有两种方式:Port和嵌入式执行。

PortErlang最基本的与外界交互的方式,进行交互的双方通过编码,解码,将信息以字节流的方式进行传递。(具体这个通道的实现方式,根据操作系统的不同而不同,比如unix环境下,采用PIPE实现,理论上任何支持对应Port通道实现的语言都可以与Erlang进行交互)。Erlang为了方便CJAVA程序员,提供了Erl_InterfaceJinterface

采用Port,您的代码在Erlang的平台之外运行,其崩溃不会影响Erlang

嵌入式执行,通过Erlang平台加载,因此这是非常危险的,如果您的程序崩溃,没有任何理由,Erlang也会崩溃。

Erlang应用场景

分布式产品,网络服务器,客户端,等各种应用环境。

Erlang也可以作为一种快速开发语言,进行原型开发。

Erlang的学习过程

1.         安装首先从Erlang官方网站,下载安装Erlanghttp://www.erlang.org/download.html

linux:获取源代码,根据说明编译;windows:直接安装

2.         认真阅读《programming erlang》(中文版图书已经问世),并不断动手练习书中的例程。

3.         遇到问题时,不要退却,坚持下去找到解决办法

4.         对语言熟悉时,浏览一些好的开源项目

5.         有信心时,开始动手做一个小项目

6.         不间断的与大家交流,共同提高

可能遇到的困难:

a)         对于语法的不适应?

坚持看下去,代码继续写下去,我相信1个月,你会喜欢上Erlang的语法

b)        有些数据类型不清楚?

认真看资料,或者询问朋友,比如我

c)        中文资料的缺乏?

Erlang中文的资料会越来越多,此外,Erlang的相关的英文资料也比较容易理解,还是那句话,别怕麻烦

Erlang开源项目

排名不分先后

  • couchdb     基于文档的数据库,拥有RestfulAPI,MVCC,View,诸多特性
  • ejabberd    性能出众,使用广泛的Jabber开源服务器
  • mochiweb    轻便,高效的HTTP应用框架
  • nitrogen    基于事件的Web开发框架
  • rabbitmq    中间服务器,实现AMQP协议
  • yaws        高效的web server
  • etorrent    Bittorrent客户端
  • Scalaris           分布式的key-value存储
  • disco       Map-Reduce框架,Erlang Python

遇到问题

参看Erlang官方文档 http://www.erlang.org/doc/

订阅Erlangmaillisthttp://www.erlang.org/mailman/listinfo/erlang-questions),进行提问

Nabble提供的Erlang maillist存档中搜索(http://www.nabble.com/Erlang-f14095.html

Google中搜索答案

推荐阅读

Erlang Design Principles http://www.erlang.org/doc/design_principles/part_frame.html

Erlang Efficiency Guide http://www.erlang.org/doc/efficiency_guide/part_frame.html

Erlang Programming Rules http://www.erlang.se/doc/programming_rules.shtml

推荐网站

http://www.erlang.org

http://erlang-china.org

http://www.planeterlang.org/

http://www.erlangplanet.org/

http://trapexit.org

[C#]再接再厉VS 2008 sp1 + .NET 3.5 sp1系列文章索引

mikel阅读(724)

[源码下载]

再接再厉VS 2008 sp1 + .NET 3.5 sp1系列文章索引

作者:webabcd
介绍
再接再厉VS 2008 sp1 + .NET 3.5 sp1系列文章索引:ADO.NET Entity Framework(实体框架), ADO.NET Data Services(数据服务), Dynamic Data(动态数据)
1、再接再厉VS 2008 sp1 + .NET 3.5 sp1(1) – Entity Framework(实体框架)之添加、查询、更新和删除的Demo
介绍
以Northwind为示例数据库,ADO.NET Entity Framework之完全面向对象的添加操作、查询操作、更新操作和删除操作

  • ADO.NET Entity Framework(EF) – 就当是微软的ORM吧,可以将概念模型映射到逻辑模型,从而使用概念模型做开发
  • Entity – 在EF中就是实体的概念
  • Entity Data Model(EDM) – 实体数据模型,表现为一组映射到数据源的实体和关系
  • LINQ to Entities – 和LINQ to XXX一样,对EF中的实体做LINQ
  • Entity SQL(ESQL) – 一种类似SQL的查询语言,针对概念模型做查询
  • csdl – Conceptual schema definition language 概念架构定义语言
  • ssdl – Store schema definition language 存储架构定义语言
  • msl – Mapping specification language 映射规范语言
  • csdl, ssdl, msl均为基于XML的语言。.edmx文件同时包含这3种语言所描述的信息
  • Entity – 实体
  • Entity Class – 实体类。至少要有一个实体键(Key Properties)
  • Entity Set – 实体集。实体集中包含着若干实体
  • Association – 关联。实体类之间的关系。导航属性就是由关联来定义的
  • Association Set – 关联集。关联集中包含着若干关联
  • Entity Container – 实体容器。实体容器中包含着若干实体集和关联集
  • 概念模型中必须要有实体键,实体键与表中的主键相对应,所以表中必须要有主键
  • 存储模型的字段如果有主键键或非空约束,则概念模型中必需要有相应的属性与之映射
  • 概念模型中存在的属性,必须在存储模型中有与之映射的字段
  • 作为映射条件的字段不能映射到属性上
  • 要实现继承的话,就要使用映射条件来进行筛选
  • EntityConnection – 与存储模型的连接。相当于SqlConnection
  • EntityCommand – 对 EDM 执行的命令。相当于SqlCommand
  • EntityDataReader – 以只读、只进的数据流的方式获取数据(内存中始终只有一行)。相当于SqlDataReader

2、再接再厉VS 2008 sp1 + .NET 3.5 sp1(2) – Entity Framework(实体框架)之详解 Linq To Entities 之一
介绍
以Northwind为示例数据库,ADO.NET Entity Framework之Linq To Entities

  • First – 返回集合中的第一个成员;不延迟
  • FirstOrDefault – 返回集合中的第一个成员(找不到则返回null);不延迟
  • All – 是否集合中所有成员都满足某一条件;不延迟
  • Any – 集合中是否有成员满足某一条件;不延迟
  • Average – 取平均值;不延迟
  • Sum – 求和;不延迟
  • Max – 取最大值;不延迟
  • Min – 取最小值;不延迟
  • Count – 取指定集合的成员数,返回值类型int;不延迟
  • LongCount – 取指定集合的成员数,返回值类型long;不延迟
  • Take – 获取集合的前 n 个成员;延迟
  • Skip – 跳过集合的前 n 个成员;延迟(Linq To Entities 需要先排序才能 Skip)
  • Distinct – 过滤集合中的相同项;延迟
  • Union – 连接不同集合,自动过滤相同项;延迟
  • UnionAll – 连接不同集合,不会自动过滤相同项;延迟
  • Concat – 连接不同集合,不会自动过滤相同项;延迟
  • Intersect – 获取不同集合的相同项(交集);延迟
  • Except – 从某集合中删除其与另一个集合中相同的项;延迟

3、再接再厉VS 2008 sp1 + .NET 3.5 sp1(3) – Entity Framework(实体框架)之详解 Linq To Entities 之二
介绍
以Northwind为示例数据库,ADO.NET Entity Framework之Linq To Entities

  • Select – 选择需要返回的字段
  • Where – 筛选
  • OrderBy – 正序排序
  • OrderByDescending – 倒序排序
  • ThenBy – 在 orderBy 或 orderByDescending 的基础上再正序排序
  • ThenByDescending – 在 orderBy 或 orderByDescending 的基础上再倒序排序
  • GroupBy – 分组
  • Join – 连接
  • GroupJoin – 连接后分组

4、再接再厉VS 2008 sp1 + .NET 3.5 sp1(4) – Entity Framework(实体框架)之EntityClient, ObjectQuery
介绍
以Northwind为示例数据库,ADO.NET Entity Framework之详解EntityClient, ObjectQuery

  • EntityConnection – 与存储模型的连接
  • EntityCommand – 对 EDM 执行的命令
  • EntityParameter – 配置 EntityCommand 的参数
  • EntityDataReader – 以只读、只进的数据流的方式获取数据(内存中始终只有一行)。相当于SqlDataReader
  • ObjectQuery<T> – 通过 Entity SQL 或 查询语法 或 Linq 方法对概念模型做查询
  • ObjectContext.CreateQuery<T>(Entity SQL) – 根据 esql 创建一个 ObjectQuery<T> 。(延迟)

5、再接再厉VS 2008 sp1 + .NET 3.5 sp1(5) – Entity Framework(实体框架)之ObjectContext
介绍
以Northwind为示例数据库,ADO.NET Entity Framework之详解ObjectContext, 以及事务和并发

  • ObjectContext – 以对象(这些对象是 EDM 中定义的实体类型的实例)的形式与数据进行交互
  • CreateObjectName – 实体类 的 CreateObjectName 静态方法用于创建实体类的新实例
  • AddToEntitySetName() – 将需要添加的对象添加到对象上下文中
  • SaveChanges() – 将所有更新保存到相关存储区中
  • Attach()/AttachTo() – 附加外部实体到上下文中
  • ObjectContext.Refresh() – 更新上下文数据
  • ObjectStateEntry – 维护实体状态的类
  • ObjectStateManager – 实体状态管理器

6、再接再厉VS 2008 sp1 + .NET 3.5 sp1(6) – Entity Framework(实体框架)之Entity SQL
介绍
以Northwind为示例数据库,ADO.NET Entity Framework之详解Entity SQL

  • Linq 方法上也可以使用 esql
  • 查询表达式
    • select, from, where, order by, group by, having
    • cross join, inner join, left outer join, right outer join, full outer join
    • case when then else end
  • 集合运算符
    • anyelement(expression) – 从集合中提取任意元素
    • except – 从左侧表达式的结果中删除其与右侧表达式结果中的相同项,并返回此结果
    • flatten(collection) – 将多个集合组成的集合转换为一个集合
    • intersect – 返回运算符两侧查询结果的相同项
    • [not] exists(expression) – 确定查询结果是否存在
    • [not] in {,} – 确定某值是否在某集合中
    • overlaps – 确定运算符两侧查询结果是否具有相同项
    • set(expression) – 移除重复项
    • union – 将运算符两侧查询结果连接成一个集合(移除重复项)
    • union all – 将运算符两侧查询结果连接成一个集合(包括重复项)
    • top(n) – 取前 n 条记录
  • 分页运算符
    • skip n – 需要跳过的项数,结合 order by 使用
    • limit n – 需要选择的项数,结合 order by 使用
  • 类型运算符
    • cast(expression as data_type) – 将表达式转换为另一种数据类型(使用 EntityCommand 执行查询,返回 EDM 类型;使用 ObjectQuery 执行查询,返回 CLR 类型)
    • oftype – 从查询表达式返回指定类型的对象集合,需 EDM 中继承关系的支持
    • is of – 确定表达式的类型是否为指定类型或指定类型的某个子类型,需 EDM 中继承关系的支持
    • treat – 将指定基类型的对象视为指定派生类型的对象,需 EDM 中继承关系的支持
  • 常用运算符
    • 算术运算符
      • +
      • -(减或负)
      • *
      • /
      • %
    • 比效运算符
      • >, >=, <, <=, <>, !=
      • is null, is not null
      • between and, not between and
      • like, not like
    • 通配符(应用于 like 和 not like)
      • % – 零个或零个以上的任意字符
      • _ – 任意单个字符
      • [] – 在指定范围 [a-f] 或集合 [abcdef] 中的任意单个字符
      • [^] – 不在指定范围 [^a-f] 或集合 [^abcdef] 中的任意单个字符
    • 逻辑运算符
      • and, &&
      • or, ||
      • not, !
    • 其他字符
      • — – 注释
      • . – 成员访问
      • ; – 分行
      • + – 串联字符串
  • 函数
    • 函数 – 聚合函数
      • Avg(expression) – 非 null 的平均值
      • Count(expression) – 记录总数(Int64)
      • BigCount(expression) – 记录总数(Int32)
      • Max(expression) – 非 null 的最大值
      • Min(expression) – 非 null 的最小值
      • Sum(expression) – 非 null 的总和值
      • StDev(expression) – 非 null 的标准偏差值(相对于平均值的标准偏差)
    • 函数 – 数学函数
      • Abs(value) – 取绝对值
      • Ceiling(value) – 取不小于参数的最小整数
      • Floor(value) – 取不大于参数的最大整数
      • Round(value) – 取参数的整数部分
    • 函数 – 字符串函数
      • Left(string, length) – 从左侧开始,取 string 的前 length 个字符
      • Right( tring, length) – 从右侧开始,取 string 的前 length 个字符
      • LTrim(string) – 去掉 string 的左侧的空白
      • RTrim(string) – 去掉 string 的右侧的空白
      • Trim(string) – 去掉 string 的两侧的空白
      • ToLower(string) – 将 string 全部转换为小写
      • ToUpper(string) – 将 string 全部转换为大写
      • Concat(string1, string2) – 串联 string1 和 string2
      • Replace(string1, string2, string3) – 将 string1 中的所有 string2 都替换为 string3
      • Reverse(string) – 取 string 的反序
      • Substring(string, start, length) – 从 string 的 start 位置开始取 length 个字符,索引从 1 开始
      • IndexOf(string1, string2) – string1 在 string2 中的位置,索引从 1 开始,若找不到则返回 0
    • 函数 – 日期和时间函数
      • Year(expression) – 取时间的年的部分
      • Month(expression) – 取时间的月的部分
      • Day(expression) – 取时间的日的部分
      • Hour(expression) – 取时间的时的部分
      • Minute(expression) – 取时间的分的部分
      • Second(expression) – 取时间的秒的部分
      • Millisecond(expression) – 取时间的毫秒的部分(0 – 999)
      • CurrentDateTime() – 取服务器的当前时间
      • CurrentUtcDateTime() – 取服务器的 UTC 当前时间
      • CurrentDateTimeOffset() – 返回值类型为 DateTimeOffset , 取当前时间及相对于 UTC 时间的差值
    • 函数 – 按 位 运算的函数
      • BitWiseAnd(value1, value2) – 取 value1 和 value2 的位与结果
      • BitWiSEOr(value1, value2) – 取 value1 和 value2 的位或结果
      • BitWiseXor(value1, value2) – 取 value1 和 value2 的位异或结果
      • BitWiseNot(value) – 取 value 的位求反结果
    • 函数 – 其它函数
      • NewGuid() – 返回新生成的 GUID
  • 不常用运算符
    • row, multiset, createref, deref, key, ref, navigate

7、再接再厉VS 2008 sp1 + .NET 3.5 sp1(7) – Data Services(数据服务)
介绍
以Northwind为示例数据库,演示ADO.NET Data Services(数据服务)

  • DataService – ADO.NET 数据服务的主入口点。 T 为数据源类名
  • IDataServiceConfiguration.SetEntitySetAccessRule(string name, EntitySetRights rights) – 为指定实体集设置访问规则
  • QueryInterceptorAttribute – 声明在方法上的查询拦截器
  • DataServiceContext – 数据服务的上下文
  • DataServiceQuery – 以指定的 URI 语法查询数据服务

8、再接再厉VS 2008 sp1 + .NET 3.5 sp1(8) – Dynamic Data(动态数据)
介绍
以Northwind为示例数据库,演示Dynamic Data(动态数据)

  • MetaModel – 数据库和域对象之间的映射的抽象
  • MetaModel.RegisterContext() – 使用指定的配置上下文注册指定的数据上下文
  • Scaffold – 译为基架。即基于数据库架构(linq to sql 或 entity framework)生成网页模板的机制
  • ScaffoldTableAttribute(false) – 隐藏指定的表
  • ScaffoldColumn(false) – 隐藏指定的字段
  • MetadataTypeAttribute(Type metadataClassType) – 指定要与数据模型类关联的元数据类
  • DynamicField – 显示指定的动态数据字段,相当于 BoundField
  • DynamicControl – 通过指定的字段模板显示指定的动态数据字段

OK
[源码下载]

[C#]关于两个MVC示例的思考(MVCStore和Oxite)

mikel阅读(858)

      最近看了一些关于MVC框架的东西,加以之前就研究过一些关于 MVC架构的信息,碰巧在网上又看
到了这样一篇文章,是关于微软内部的开发者对Oxite项目的个人攻击,让我产生了写篇文章来表达一 
下自己对于这种架构模式的思考。
    声明,如果之前没看过这两个项目的朋友建议下载相应的源码:
    MVCStore:http://www.codeplex.com/mvcsamples
    Oxite:http://www.codeplex.com/oxite
    好了,开始今天的正文:)
1.Controller干了些什么
     先说一下我的看法,这个所谓控制器的最大作用应该是“控制和调度”,控制即前台视图(view)的
显示(显示那个视图), 调度即执行相应的业务逻辑 (在这两个项目中就是那些Services,而Services
即完成对model数据模型的封装调用,并实现相关的业务逻辑)。这里业务规则如何定义应该是在Ser-
vices里进行,与Controller无关。
     就其工作性质而言还是比较简单的,因此简要的工作内容就应该有简单的实现(指代码),这里可以
看看MVCStore是如何搞的,请见下面代码:
  (摘自Commerce.MVC.Web"App"Controller"AuthenticationController.cs):

public class AuthenticationController : Controller
{
        .
        
public ActionResult Login()
        {
            
string oldUserName = this.GetUserName();
            
string login = Request.Form["login"];
            
string password = Request.Form["password"];
            
if (!String.IsNullOrEmpty(login) && !String.IsNullOrEmpty(password))
            {
                var svc 
= new AspNetAuthenticationService();
                
bool isValid = svc.IsValidLogin(login, password);
                
//log them in
                if (isValid)
                {
                    SetPersonalizationCookie(login, login);
                    
//migrate the current order
                    _orderService.MigrateCurrentOrder(oldUserName, login);
                    
return AuthAndRedirect(login);
                }
            }
            
return View();
        }
    
}

     一看便知这是一个登陆验证操作,其使用Request.Form方式从表单中获取数据,这里暂不说其获取的方式
优不优雅(因为与本文要聊的内容关系不大)。可以看出其实现的过程也之前采用webform方式开发出现的代码
也差不多,只不过是将相应的login.aspx.cs中的操作放到这controller中,这种好处主要就是将原本分散但功
能上应该同属于认证的类(Authentication类是按架构设时划分出来的)放置在了一起,这样在代码分布上会
更合理一些。另外就是进行单元测试时也会很容易编写测试代码。当然还有好处,我想就是将那些经常变化的
代码使用这种方式约束在了controller中,为将来的后续开发,特别是维护以及查找BUG上会有一个比较清晰的
范围。
     当然在看Oxite代码时,这块会有所差异,即Oxite使用了IModelBinder来实现将表单中的数据绑定到相应
的类上以完成Model中(实体)类的初始化绑定工作,如下:

public class UserModelBinder : IModelBinder
{
    
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        NameValueCollection form 
= controllerContext.HttpContext.Request.Form;
        User user 
= null;
        Guid siteID 
= Guid.Empty;
        
if (!string.IsNullOrEmpty(form["siteID"]))
        {
            form[
"siteID"].GuidTryParse(out siteID);
        }
        
if (siteID == Guid.Empty)
        {
            user 
= new User
            {
                Name 
= form["userName"],
                Email 
= form["userEmail"],
                DisplayName 
= form["userDisplayName"],
                Password 
= form["userPassword"]
            };
            Guid userID;
            
if (!string.IsNullOrEmpty(form["userID"]) && form["userID"].GuidTryParse(out userID))
            {
                user.ID 
= userID;
            }
        }
        
return user;
    }
}

     其实这种做法有一定的好处,就是将这类在功能上类似的操作进行了封装。比如大家可以想像通过web请求提交过来
一个大表单,上面有几十个字段属性(不要问我为什么会有这种大表单),而如何将这些字段绑定的操作与controller中
接下来的业务流程操作放在一起,会让controller中的相应方法代码长度增长过快,所以倒不如通过上面这个方法将相应
的实体类与Web页面信息的绑定工作分离出来,当然 Oxite使用声明相应UserModelBinder类的方式进行封装的做法还
有待商榷
     说来说去,还是如Rob Conery所说的,要始终保持用Controller与View的轻快,易于测试这一原则。这一点其实正
与SOA架构中的一些思想相符合,下面结合我的理解来解释一下:
     在SOA中,有组件(component)的概念,即组件是业务逻辑的原子级功能操作,其按照高内聚低耦合的方式进行设计,
当业务流程开发运作时,其工作原理就是正确组合相应的业务组件来实现相应的应用。这样的好处就是可以将这些组件分
布式布署,同时当业务流程发生变化时,只要调整相应的业务流程逻辑(soa中称为bpel)即能够快速响应业务变化。而
如何构造这种可重用的组件在SOA中也是有相应规范的,被称为SCA.
     说到这里有些远了,那在MVC架构中又有什么类似的思想呢?其实在这里controller的一些设计要求与SOA中的bpel
有着相似的设计理念,即完成对业务组件(即MVCStore解决方案中的Commerce.Services项目下的相应文件)的流程
编排和调用。这样即便将来需求变化,而导致了业务流程的变化(不是业务规则变化),也只是修改Controller这一层应
该可以满足了,而正确而快速的修改的“前提”,应该就是该Controller应该设计得“尽可能的轻快”。
 
2.需求变化了,导致了业务规则变化怎么办?
   
     正如Ivar jacbson 在传授“明智开发”模型时所说的那样,“软件开发中不变的是–需求的不断变化”。这一点相信
大家是有强烈共鸣的。
     这里我们先来简单的看一下MVCStore和Oxite的处理方式,从设计思路上两个项目基本一致,即使用接口分离方式来
应对这种变化。比如:Oxite"Services"中就有这样的代码:

public class UserService : IUserService
{
      
private readonly IUserRepository repository;
      
private readonly IValidationService validator;
      
public UserService(IUserRepository repository, IValidationService validator)
      {
          
this.repository = repository;
          
this.validator = validator;
      }
      
#region IUserService Members
      
public User GetUser(string name)
      {
          
return repository.GetUser(name);
      }
      
public User GetUser(string name, string password)
      {
          User user 
= string.Compare(name, "Anonymous"true!= 0 ? repository.GetUser(name) : null;
          
if (user != null && user.Password == saltAndHash(password, user.PasswordSalt))
              
return user;
          
return null;
      }
      
public void AddUser(User user, out ValidationStateDictionary validationState, out User newUser)
      {
          validationState 
= new ValidationStateDictionary();
          validationState.Add(
typeof(User), validator.Validate(user));
          
if (!validationState.IsValid)
          {
              newUser 
= null;
              
return;
          }
       .
}

      其实现了IUserService服务接口。
      而MVCStore中的Commerce.Services项目中的代码也使用了类似接口定义,比如:

[Serializable]
public class OrderService : Commerce.Services.IOrderService {
    IOrderRepository _orderRepository;
    ICatalogRepository _catalogRepository;
    IShippingRepository _shippingRepository;
    IShippingService _shippingService;
    
public OrderService() { }
    
public OrderService(IOrderRepository rep, ICatalogRepository catalog,
        IShippingRepository shippingRepository, IShippingService shippingService)
    {
        _orderRepository 
= rep;
        _catalogRepository 
= catalog;
        _shippingRepository 
= shippingRepository;
        _shippingService 
= shippingService;
    }
    
/// <summary>
    
/// Gets all orders in the system
    
/// </summary>
    
/// <returns></returns>
    public IList<Order> GetOrders() {
        
return _orderRepository.GetOrders().ToList();
    }
 .
}

     定义并实现这些服务接口之后,就可以通过IOC这类方式来实现最终的注入,以决定在程序运行时使用那些具体
实现类了,比如Oxite中的Oxite/ContainerFactory.cs是这样进行注册的(使用了Unity框架):

public IUnityContainer GetOxiteContainer()
{
     IUnityContainer parentContainer 
= new UnityContainer();
     parentContainer
         .RegisterInstance(
new AppSettingsHelper(ConfigurationManager.AppSettings))
         .RegisterInstance(RouteTable.Routes)
         .RegisterInstance(HostingEnvironment.VirtualPathProvider)
         .RegisterInstance(
"RegisterRoutesHandler"typeof(MvcRouteHandler));
     
foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
     {
         parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
     }
     parentContainer
         .RegisterType
<ISiteService, SiteService>()
         .RegisterType
<IPluginService, PluginService>()
         .RegisterType
<IUserService, UserService>()
         .RegisterType
<ITagService, TagService>()
         .RegisterType
<IPostService, PostService>()
         .RegisterType
<ITrackbackOutboundService, TrackbackOutboundService>()
     ..
}

    当然这种做法是有普遍性的,好处也是很明显。就是将来如果业务规则变化时(对应service接口实现类
也要发生变化),这时不需要真正修改已有的代码,只需再开发一个相应的实现类即可满足需求,这种扩展
方式也是与设计模式中的思想相符合的。
    说到这里,把话题再深入一下,就是微软模式与实践小组的Service Layer Guidelines中对象这块还会
有一个Application Facade(在其Business层中),如下图:
   
     其完成的是对这些service组件的“应用层面级”封装,说的再白一些,其可以包括对业务工作流,业务
实体,业务组件的三者的封装。以便于对外实现(暴露)统一的服务访问接口。就这部分而言,MVCStore
做的比Oxiete要好,其在工作流中对各类已定义的服务组件的逻辑调用写的很有味道,比如Commerce.-
Services项目下的 AcceptPayPalWorkflow.csShipOrderWorkflow.cs
     当然就目前工作流的作用远不止这些,必定其也可以采用WCF服务的方式把自己暴露给外界。就这一
点,其自身也可以转化为一个服务组件,到这里就出现了一个有趣的现象,即:
     已将一些服务组件囊括的工作流自己也成了一个服务组件而被其它服务组件所调用。不是吗?
     在SOA架构中,这种情况是很普遍的,因为组件是一些基本的业务规则逻辑,其应允许被其它组件访问
甚至包含以使业务规则更加清晰,说白了就是可复用性。
   
     对开发者而言只有这样才可能提升开发速度(重用已有组件的好处不仅仅是少写代码,还包括测试和布
署等方面的成本也会降低),这一点想一想那些开源的框架就会理解了。而对于企业管理者而言就是保护“
已有投资”

3.两个项目中的困惑

   
     的确,看了这两个MVC之后,还是有些让我感觉不是太清晰的地方,比如MVCStore中,Commerce.Data
项目下的Model/Order.cs类,我刚开始一看,还真被震住了,很有充血模型的味,下面是部分代码:

Code

     可正当我带着兴趣去观察其它相应的域模型类时,又回到了贫血域模型。不是吗?的方法是要感觉好像
不是一个开发人员写的才会出现这种情况,因为按其架构设计上来看,这个类中被放到Serivce中实现的,
因为我不是该项目的开发人员,想不出个所以然来。
     白乎了这些,其它在这两个项目中还有一些差异,当然本文开头提到的那篇文章也说出了一些“问题”。
不过还是那句话,没有最好的设计只有最适合的设计,这两个项目都有可圈可点的地方,但对自己所在公
司部门是不是“完全适合”只能结合自己团队的情况而定了。
     比如说关于Commerce.MVC.Web中将controller和view放在了一起,就是个问题,比如在团队中
有如下分工:
     VIEW开发人员 + Controller开发人员 + Service组件开发
     那么将View目录与Controller目录放在不同的项目中应该是个不错的方式,起码在项目级别上将这
两类开发者进行了分离。当然有人会说,一般情况下VIEW 和Controller的设计者应该是一个人而不是
两个人, 但分工明确才能尽一步提升生产力,特别是MVC这个框架还很新,有些开发人员学习是从View
语法入手,有些人从Controller入手,有些人比如我是从Service入手。这就导致关注和侧重点不同,最
后导致自己的理解和优势也会不同。将View分离出来的好处在于发挥各自的优势,让前台开发人员可以
将精力放在与UI设计师交流设计实现,界面实现,js(目前是JQuery)封装调用等方面。相信随着项目
的不断扩大和开发人员的后续补充势必会造成这样的问题。
     好了,今天的内容就先到这里了。
     
    
     原文链接:http://www.cnblogs.com/daizhj/archive/2009/02/26/1398689.html
     作者: daizhj, 代震军
     Tags: soa,mvc,sca,bpel
     网址: http://daizhj.cnblogs.com

[JQuery]Rich IntelliSense for jQuery

mikel阅读(931)

Update: A new Hotfix is availble to complement this file.

A while back we updated VS2008 IntelliSense to not fail when referencing JQuery.  However, getting IntelliSense for chained calls and rich summary/parameter hints still required adding special comments to a few thousand lines of JQuery.  If you didn't have the time, you could download such a file from friendly members of the community such as James and Brennan.

As part of our new partnership with jQuery, yesterday we announced the availability of the official IntelliSense documentation file.  As you can see, our friends at jQuery have added a new download link for Visual Studio at http://docs.jquery.com/Downloading_jQuery#Download_jQuery.

image

You can also download the file directly from http://code.jquery.com/jquery-1.2.6-vsdoc.js.  As you might guess, this documentation file corresponds with the latest version of jQuery (which is currently 1.2.6).  While this file has a "js" extension, it's really just a documentation file.  You do not want to run this file in the browser.

How do I use this file (today)?

If you're inside an ASPX page, you will need to add the following lines of code into (normally) the head of your page:

image

Why do we have a server-side conditional statement?  IntelliSense disregards conditional statements of this type, and thus loads the "vsdoc" file (overriding the normal one).  At runtime the if (false) statement will ensure it this documentation file is not rendered (and executed) as script.  This trick allows the "switching" behavior you want.

If you're in a JavaScript file, use the normal file reference syntax to refer to the "vsdoc" file.

image

There's no need for tricks here since this comment is only meaningful to IntelliSense.

How do I use this file (in the near future)?

The ideal user experience should be one where you do not need special tricks as mentioned above.  Really, you shouldn't need to mention the "vsdoc" file name at all.  To that end, we plan on releasing a Hotfix that will enable this (stay tuned!).  Given normal references such as…

image image

…IntelliSense will opportunistically search for "jQuery-1.2.6-vsdoc.js" and load that file instead.  Generally, given script name "x", IntelliSense will opportunistically search for "x-vsdoc.js".  If not found, it will then search for "x.Debug.js".  You just need to make sure to place your "vsdoc" file next to the normal file.  Note, if you use jquery-1.2.6.min.js, you may need to rename the file to match the search pattern.

What are the advantages of this file?

One unique benefit of the file we have released is that it supports and understands jQuery plug-ins.  For example, given the plug-in below…

image

…you would see "myplugin" show up in IntelliSense.

image

We've noticed a few plug-ins do not work, and commonly this is because there is an IntelliSense incompatibility issue with the plug-in itself (as opposed to the jQuery documentation file).

Hope this helps!

Jeff King
Program Manager
Visual Studio Web Tools

[JQuery]VSDoc for jQuery 1.3.2 插件公布

mikel阅读(920)

JQuery 1.3 was released a little over a month ago.  The 1.3 release adds exciting features such as selector performance improvements, "live events" where events can be bound to both current and future elements, and more.  Version 1.3.2 was released a few days ago and fixes some bugs include one involving selector performance in IE.

I just wanted to let everyone know that we now have VSDoc files for all of the 1.3 releases.  You can download them from the jQuery Downloads page

jquerycom

A refresher on how to use the file can be found here or here.  Here's one of the new functions in action…

jqueryedit

Hope this helps!

Jeff King
Program Manager
Visual Studio Web Tools

[Javascript]document.getSelection,document.selecti

mikel阅读(931)

<script>
function getSel()
{
      var txt = '';
      var foundIn = '';
      if(window.getSelection)
      {
          txt = window.getSelection();
          foundIn = 'window.getSelection()';
      }else if(document.getSelection)
      {
          txt = document.getSelection();
          foundIn = 'document.getSelection()';
      }else if(document.selection)
      {
          txt = document.selection.createRange().text;
          foundIn = 'document.selection.createRange()';
      }else
          return;
      document.forms[0].selectedtext.value = 'Found in: ' + foundIn + '\n' + txt;
}
</script>
<form>
<textarea name="selectedtext" rows="5" cols="50">
</textarea><br>
<input type="button" value="Getselection" onmousedown="getSel()">
</form>
以上的代码可以捕获到选中的网页中的纯文本内容(不含HTML标签)
如果想获得包含html的内容,将document.selection.createRange().text改成document.selection.createRange().htmlText
 

[PHP]wordpress划词评论插件

mikel阅读(789)

发现一个用于Wordpress划词评论插件
下载地址:
http://code.google.com/p/select-text-comment/downloads/list
使用方法:

上传/激活

把”<?php add_text_comment();?>”这段代码复制到模板里的”single.php”,比如放到”get_sidebar()”之前。然 后还是在”single.php”里,找个合适的地方放上”<span id=”cloSEOpenselctcmt”>关闭划词评论</span>”这段。这样就算大功告成了。

如果觉得不好看,可以自己修改样式。

[Javascript]测试划词搜索

mikel阅读(732)

<script>
document.body.onload 
= tt;
function tt()
{
alert('dddddddd');
}
var b;
function te() //onselect事件
{
b
=1;
}
function a() //onmouseup事件
{
if (b==1){
alert(document.selection.createRange().text);
b
=0;
}
}
</script>
<body onselect ="JavaScript:te();" onmouseup ="JavaScript:a();">
 onselectstart ="alert('开始');"
onmouseup="alert(document.selection.createRange().text);"
//firefox使用 window.getSelection()
网页测试测试测试才儿时才扼杀
<br>
dddddddddddddddd
<SCRIPT   LANGUAGE="JavaScript">   
  
function   SelText(){   
  document.selection.createRange();   
  document.all.TestText.value   
=   unescape(document.selection.createRange().text);   
alert(
"选区左侧宽度:"+ document.selection.createRange().boundingLeft);
alert(
"选区上方高度:" + document.selection.createRange().boundingTop);
alert(
"选区高度:" + document.selection.createRange().boundingHeight);
alert(
"选区宽度:" + document.selection.createRange().boundingWidth);
  }   
  
</script>   
  
<input   type="text"   id="TestText"   name="TestText"   value=""   accesskey="A">   
  
<input   type="button"   name="btnTest"   value="Test"   onclick="SelText()">
<hr>
对象: document.selection.createRange();  返回TextRange 对象
<br>
TextRange 属性
<br>
boundingHeight 获取绑定 TextRange 对象的矩形的高度。 
<br>
boundingLeft 获取绑定 TextRange 对象的矩形左边缘和包含 TextRange 对象的左侧之间的距离。 
<br>
boundingTop 获取绑定 TextRange 对象的上边缘和包含 TextRange 对象的顶边之间的距离。 
<br>
boundingWidth Retrieves the width of the rectangle that bounds the TextRange object.  
<br>
htmlText 获取绑定 TextRange 对象的矩形的宽度。 
<br>
offsetLeft 获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置。
<br> 
offsetTop 获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置。 
<br>
text 设置或获取范围内包含的文本。 
<br>
</body>

[Javascript]Js划词搜索实现

mikel阅读(674)

网上流传的Js版划词搜索代码浏览器兼容上有问题,经过两天的改写终于做出可兼容非IE的版本.
JS代码
默认搜索引擎为:google
转自: js中国 http://www.javascriptcn.cn
http://JavaScriptxml.5d6d.com/thread-2-1-1.html

<!– 
if (navigator.appName=="Microsoft Internet Explorer") {
document.body.onload
=adddiv;  
document.onmousedown
=recordobj;  
document.ondblclick
=dbclick;  
document.onmouseup
=showselect;
else 
{
//firefox
window.onload=adddiv();  
window.onmousedown
=recordobj;  
window.ondblclick
=dbclick();  
window.onmouseup
=showselect; 

///////////////////////////////////////////////////////////////////////////////////  
var starobj,
isdb
=false,
allow
=true;
///////////////////////////////////////////////////////////////////////////////////  
function isallow()  
{  
if(allow){  
allow
=false;  
alert('is closed');  
}  
else{  
allow
=true;  
alert('is opend');  
}  
}
////////////////////////////////////////////////////////////////////////////////////  
function dbclick()  
{  
isdb
=true;  
}
/////////////////////////////////////////////////////////////////////////////////////  
//
function recordobj(evt)  
//
{  
//
starobj=event.srcElement;   //IE
//
starobj=evt.target
//


function recordobj(evt)  {  
evt 
= (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
          starobj 
= (evt.target) ? evt.target : evt.srcElement
}
//starobj=evt.srcElement;  

//////////////////////////////////////////////////////////////////////////////////////// 
function showselect(evt) {  
//test
var obj;
var strlen;
evt 
= (evt) ? evt : ((window.event) ? window.event : "")
if (evt) {
if (evt.target) 
{
obj 
= evt.target;
strlen 
= window.getSelection().toString();
}
else
{
obj 
= event.srcElement;
strlen 
= document.selection.createRange().text;
}
}
//test
var str="";  
if(obj.tagName!="A"&&obj.tagName!="INPUT"&&obj==starobj&&!isdb&&allow)  
{  
//var oText=document.selection.createRange();  //IE
//
alert(window.getSelection());
//
alert(window.getSelection().toString().length);
//
if(window.getSelection().length>0)  //错误写法
//
//////////
 
//if(window.getSelection().toString().length>0)
if(strlen.length>0)

//alert("ddd"+window.getSelection().boundingWidth());
//
alert("dddddddddddddddddddd000");
 str=strlen;  
//oText.text="BuB"+oText.text+"EuE";  
}  
//oText.select();  
//
window.getSelection.select()
}
//alert("自负" + str);
  
searchgoogle(str,evt);  
isdb
=false;  
}
//////////////////////////////////////////////////////////////////////////////////////////  
function searchgoogle(str,evt)  
{  
var obj=document.getElementById("searchgoogle");  
//alert("长度"+str.toString().length);
if(str.toString().length>0)  
{
//alert("tongguo");  
//
alert(obj);
obj.style.display="block";  
obj.style.position
="absolute";  
obj.style.zindex
=999
//alert("y" + evt.clientY + "X" + evt.clientX); 
//
//
alert(document.body.scrollTop.toString()); 
//
alert("new" + document.documentElement.scrollTop.toString());
//

if(navigator.appName=="Microsoft Internet Explorer")
{
obj.style.widht
=80;
obj.style.posTop
=document.body.scrollTop+event.y25;  
obj.style.posLeft
=document.body.scrollLeft+event.x+5;   
}
else
{
obj.style.width 
= "100px"
obj.style.top
=evt.pageY25;
obj.style.left
=evt.pageX+5

 
 
obj.innerHTML
="<a target=_blank href=http://www.google.com/search?ie=UTF-8&oe=UTF-8&q="+encodeURI(str)+" style='BORDER- RIGHT: royalblue thin solid; BORDER-TOP: royalblue thin solid; FONT- WEIGHT: bold; BORDER- LEFT: royalblue thin solid; CLIP: rect(auto auto auto auto); COLOR: #ffffff; BORDER- BOTTOM: royalblue thin solid; BACKGROUND-COLOR: inactivecaption; TEXT- DECORATION: none'>Search It!</a>";  
}  
else  
{  
obj.style.display
="none";  
}  
}
///////////////////////////////////////////////////////////////////////////////////////////////  
function adddiv()  
{
var mobj = document.createElement("div");  
mobj.id
="searchgoogle"
document.body.appendChild(mobj);  
}  
//–>  

直接在html页面中调用即可实现网页划词搜索功能.