[转载]C#字符串与享元(Flyweight)模式

mikel阅读(871)

[转载]C#字符串与享元(Flyweight)模式 – winter-cn – 博客园.

写这个文章,主要是因为网上对C#字符串和享元模式的误解比较多。

Flyweight模式

先说这名字,fly呢,就是苍蝇,没错这里面不是飞的意思,是苍蝇的意思,weight大家都知道,就是重量,苍蝇的重量,就是非常非常轻的意思。所以Flyweight模式就是处理非常非常轻量级对象的一个东西。

Flyweight的目标是解决大量细粒度对象的内存消耗问题,当然,巧妇难为无米之炊,任何模式和手法都不能凭空造出内存来,所以享元模式针对的情况是这些细粒度对象的中数据有重复的情况。

Flyweight的做法是,把对象的状态(通常用属性表示),分成两个部分,一部分是内部状态,另一部分是外部状态。内部状态是不易重复的(或者说必要的),外部状态是易重复的。所以,Flyweight把外部状态提取出来共享,这样就一定程度解决了内存占用问题。

C#中的字符串不是Flyweight模式

在网上常常可以看到一个说法,说C#中的字符串使用了Flyweight模式,开门见山地说,这个说法是错误的。

错在哪里呢?按照上文的介绍,错就错在字符串它没有所谓的“内部状态”。

通常讲字符串是享元的原因就是以下代码:

string a = “Hello World”;
Console.WriteLine(Object.ReferenceEquals(a, “Hello World”)); //True

当使用字符串直接量的时候,不论你写了多少个”Hello World”,最终内存里面只有一个字符串对象。

运行时创建的字符串并不在此列,可以使些手段,强制在内存里面产生新的字符串。

string a = “Hello World”;
Console.WriteLine(Object.ReferenceEquals(a, new String(“Hello World”.ToCharArray())));  //False

因为我们强行调用了new,所以这个字符串跟内存中的直接量”Hello World”对应的对象不是同一个。

有趣的是,C#还允许强制把一个字符串加入到(如果已经有了,就只是找出来)字符串池里面。

string a = “Hello World”;
string b = String.Intern(new String(“Hello World”.ToCharArray()));
Console.WriteLine(Object.ReferenceEquals(a,b) );

或者

string a = String.Intern(new String(“Hello World”.ToCharArray()));
string b = String.Intern(new String(“Hello World”.ToCharArray()));
Console.WriteLine(Object.ReferenceEquals(a,b) );

前面提到了,这个行为跟Flyweight使用的内部状态和外部状态不同,是两个对象实实在在就是同一个对象。

C#中的字符串与Flyweight模式

好吧,前面说了不少,C#中的字符串不是Flyweight模式,但是是不是就意味着C#里面字符串跟Flyweight没有关系呢?

当然不是,否则我写这么一篇文章岂不是太蛋疼了……

字符串池和Intern方法简直是实现Flyweight的神器啊!

考虑我们有某一类对象,可能会创建几百万个,对象里面恰巧有这么一个属性叫做颜色,它在对象构造的时候随机产生,颜色用的是rgb色,用rgb24来表示,于是颜色字符串类似#ccc这样子。

代码写起来就像下面的样子:

    class Element
    {
	static Random rnd = new Random();
	static char[] table;
	static Element() 
	{
	    table = "0123456789abcdef".ToCharArray();
	}

	public string color;
	public Element()
	{
	    color = "" + table[rnd.Next() % 16] + table[rnd.Next() % 16] + table[rnd.Next() % 16];
	}
    }

接下来我们创建3千万个对象看看如何

	    Element[] eles = new Element[30000000];
	    for (var i = 0; i < 30000000; i++)
	    {
		eles[i] = new Element();
	    }

从任务管理器看到一大块内存被吃掉了

QFOMR9}(NR%(T3`V3Q35MSY

接下来我们使用String.Intern来实现Flyweight:

    class Element
    {
	static Random rnd = new Random();
	static char[] table;
	static Element() 
	{
	    table = "0123456789abcdef".ToCharArray();
	}

	public string color;
	public Element()
	{
	    color = String.Intern("" + table[rnd.Next() % 16] + table[rnd.Next() % 16] + table[rnd.Next() % 16]);
	}
    }

同样来看看运行结果:
@XMI6L75IKU}S%NGI6L31@K

可以看到内存占用量的明显变化。

因为字符串对象的不可更改性质,使用了String.Intern之后,我们完全看不出前后color的区别,也就是说,修改前后的Element类是完全等效的,但是Flyweight为我们节约了大量的内存。

更多思考

这个典型的使用flyweight场景为我们揭示了享元外部状态的特征:像字符串一样不可更改的对象。GoF原书的例子中的字型对象Glyph也是如此。

String.Intern这种对象池的方式实现flyweight也值得借鉴,我们可以考虑自己设计flyweight的外部状态对象时使用类似的方式。

[转载]WCF RESTful服务的Google Protocol Buffers超媒体类型

mikel阅读(1141)

[转载]WCF RESTful服务的Google Protocol Buffers超媒体类型 – 张善友 – 博客园.

Protocol Buffers 是在一个很理想的结构化数据的语言中立的序列化格式。你可以考虑一下XML或JSON,但更轻,更小的协议缓冲区。 这种格式的广应用于谷歌不同的系统之间交换数据。

由于其结构化数据的最佳表现,protocol buffers 是一个代表RESTful服务处理的数据很好的选择。要遵循REST的原则, protocol buffers 应作为一个新的超媒体类型的代表。 在当前版本(.NET 4) 的Windows通讯基础(WCF),包含一个新的媒体类型,需要相当数量的努力。 幸运的是,新版本的WCF HTTP堆栈,使媒体类型的WCF编程模型的一等公民,大家可以Glenn Block’s 博客去了解更详细的内容。推荐大家假期可以看下这本书《REST实战》http://book.douban.com/subject/6854551/

下面我们来介绍如何使用Google Protocol Buffers,只定义一个超媒体类型 ProtoBufferFormatter:

自 定义超媒体类型是通过创建自定义的MediaTypeFormatter,实现OnWritetoStream() 和 OnReadFromStream() 方法进行序列化和反序列化处理。人们经常认为媒体类型只是在服务端使用,但是它用来在客户端控制序列化和反序列化的要求,下图显示了一个HTTP 请求/响应和媒体类型格式化扮演的角色:

MediaTypeFormatterProcess

这个例子我们使用入门:构建简单的Web API 的代码和WCF Web API Preview 6。使用的媒体类型是application/x-protobuf ,REST服务的核心原则就是服务器和客户端之间的松耦合性,客户端需要知道书签的URI,但不应该知道任何其他的URI的知识,但是客户端必须知道链接关系。

image

下面的代码是自定义的ProtoBufferFormatter,构造函数里指明了支持的媒体类型 application/x-protobuf。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http.Formatting;
using System.IO;
using ProtoBuf;
using ProtoBuf.Meta;

namespace WcfWebFormat.Formatters
{
public class ProtoBufferFormatter : MediaTypeFormatter
{
public ProtoBufferFormatter()
{
this.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue(“application/x-protobuf”));
}

protected override void OnWriteToStream(Type type, object value, Stream stream, System.Net.Http.Headers.HttpContentHeaders contentHeaders, System.Net.TransportContext context)
{
Serializer.Serialize(stream, value);
}

protected override object OnReadFromStream(Type type, Stream stream, System.Net.Http.Headers.HttpContentHeaders contentHeaders)
{
object obj = (RuntimeTypeModel.Default).Deserialize(stream, null, type);
return obj;
}

}
}

如上所示,我们在OnWriteToStream方法中将.NET对象序列化为ProtoBuf格式,在OnReadFromStream方法中将ProtoBuf格式饭序列化为.NET对象。

现在需要给我们的.NET对象加入ProtoBuf 序列化的标签:

using System.Collections.Generic;
using System.Xml.Serialization;
using ProtoBuf;

namespace ContactManager.Resources
{
[ProtoContract]
public class Contact
{
[ProtoMember(1)]
public int ContactId { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
}
}

把ProtoBufferFormatter 加入到WCF运行时的超媒体类型集合里。

using Microsoft.ApplicationServer.Http;
using WcfWebFormat.Formatters;

namespace ContactManager
{
public class ContactManagerConfiguration : HttpConfiguration
{
public ContactManagerConfiguration()
{
this.Formatters.Add(new ProtoBufferFormatter());
}
}
}

修改服务配置,使用ContactManagerConfiguration:

var config = new ContactManagerConfiguration() { EnableTestClient = true };
routes.Add(new ServiceRoute(“api/contacts”, new HttpServiceHostFactory() { Configuration = config }, typeof(ContactsApi)));

在客户端调用的代码如下:

var serviceUri = new Uri(“http://localhost:9000/api/contacts/”);
var httpClient = new HttpClient();
httpClient.BaseAddress = serviceUri;
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(“application/x-protobuf”));

var response = httpClient.GetAsync(“1”).Result;
Contact obj = (RuntimeTypeModel.Default).Deserialize(response.Content.ReadAsStreamAsync().Result, null, typeof(Contact)) as Contact;

var formatters = new MediaTypeFormatterCollection() { new ProtoBufferFormatter() };
var content = new ObjectContent<Contact>(obj, “application/x-protobuf”,formatters);
content.Headers.ContentType = new MediaTypeHeaderValue(“application/x-protobuf”);

httpClient.PostAsync(serviceUri,content);

即使目前来说Google Protocol Buffers没有XML/JSON那样普及,RESTful服务使用中ProtoBuf无疑是一个非常有效的超媒体类型。祝大家龙年新春愉快,吉祥如意!

相关文章:

[转载]Android读写文件基于Java的文件输入输出流

mikel阅读(1201)

[转载]Android读写文件基于Java的文件输入输出流 – 小文字 – 博客园.

一、向本地默认地址存储文件

1 public void save(String filename, String content) throws Exception 
2 { 
3       FileOutputStream outStream = context.openFileOutput(filename, Context.MODE_PRIVATE); 
4       outStream.write(content.getBytes()); 
5       outStream.close(); 
6 }

首先创建一个文件输出流对象,它的值等于上下文context调用openFileOutput()方法的返回值,这个openFileOutput()方法将为本应用打开指定的私有文件以供写操作,如果当前文件不存在将创建一个。

方法的两个参数分别指定了文件的名称和读写模式,其中文件名称不能包含路径,文件的位置在:data/data/<package name>/files里面(这个目录可以用getFilesDir()方法得到,类似的getCacheDir()方法可以得到和files同级的cache目录)。读写模式分为4种基本模式,分别是:

MODE_PRIVATE或0 即私有模式:默认模式,文件只能被当前应用操作,而对其它应用透明。如果文件已经存在,新数据会把原有数据覆盖。

MODE_APPEND或32768 即附加模式:文件只能被当前应用操作,而对其它应用透明。如果文件已经存在,新数据会添加在旧数据之后。

MODE_WORLD_READABLE或1 即读模式:允许其他应用对本模式下创建的文件进行读操作。

MODE_WORLD_WRITEABLE或2 即写模式:允许其他应用对本模式下创建的文件进行写操作。

如果允许其他应用对该文件读和写两种操作,参数可以为Context.MODE_WORLD_READABLE+ Context.MODE_WORLD_WRITEABLE或者3,也就是读,写的组合。

然后调用输出流对象的write()写方法,其参数是字节类型,所以要把字符串类型转换为字节,调用getBytes()方法。

最后记得关闭输出流。

二、向SD卡存储文件

1 public void saveToSDCard(String filename, String content) throws Exception 
2 { 
3      File file = new File(Environment.getExternalStorageDirectory(), filename); 
4      FileOutputStream outStream = new FileOutputStream(file); 
5      outStream.write(content.getBytes()); 
6      outStream.close(); 
7 }

首先在清单文件里配置操作SD卡的权限

1 <!-- 在SDCard中创建与删除文件权限 --> 
2 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 
3 <!-- 往SDCard写入数据权限 --> 
4 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

要想将文件存放到SD卡上面,必须指定目录,这时我们不再像之前一样调用openFileOutput()方法,因为它存放的文件是位于手机本身“内存” 里面的,利用File的构造函数可以传入文件地址,有三种常用的,参数分别是目录与文件名,绝对路径,目录路径与文件名

1 public File(File dir, String name)
2 public File(String path) 
3 public File(String dirPath, String name)

案例中通过Environment.getExternalStorageDirectory()方法获得Android手机中扩展卡的根目录,然后把file作为参数传给文件输出流的构造器,返回一个输出流,后面的操作和前面的相同。

[转载]番茄工作法——专治拖延症、精神涣散、再要五分钟综合症

mikel阅读(970)

[转载][推荐]番茄工作法——专治拖延症、精神涣散、再要五分钟综合症 – 1-2-3 – 博客园.

首先向仍然在工作岗位上奋战以及回到家还不忘刷博客园的兄弟们致敬。
最近读了一点《The Clean Coder》,一个意外的收获是,知道了原来还有个“番茄工作法”。尝试了几天,觉得很有效,推荐给你。
XX工作法,一听这名字你就能猜到,是时间管理,而时间管理又往往等同于成功学,所以你看了标题之后习惯性地无视本文的话,我也不怪你。成功学没有效果,是因为每个人的特质、境遇都不相同,成功往往无法复制。相反的,想办法改掉自己的一些坏习惯会更有效果。
我同时患有严重的拖延症、精神涣散和再要五分钟综合症,用了番茄工作法之后,好转了很多。
先说说我的病情。
拖延症——一直在心里想着“那件事必须得做了”,可就是迟迟不能开始。越是困难的、重要的事情,越容易这样。
再要五分钟综合症——无论娱乐还是工作,都容易烂尾。想好了娱乐到9点就工作,结果拖拖拉拉到10点还没开始干正经事;下定决心要在11点之前睡觉,但是天天都会磨蹭到11点半,12点还没上床。
精神涣散——我们都知道打断的危害。但是仔细想想,被别人打断的次数远没有被自己打断自己的次数多。譬如编译程序的时候,感觉好慢呐,赶快趁机刷下微博或者豆瓣吧,然后看到网友上传了美女图片,呦,韩寒写了新博客啦,完了又想起周报还没写呢……
这三种毛病综合发作的结果就是,整天愁眉苦脸、心急火燎,好像忙忙叨叨累的不行,到了该睡觉的时候却发现其实没干多少正经事,越发舍不得睡觉。脸色苍白,目光空洞,神疲气短,恶性循环。

番茄工作法

番茄工作法用一句话来概括就是——工作25分钟休息5分钟。详细条款如下:
0. 准备工作:买一个厨房计时器,或者使用软件计时器也行pcmaciphoneAndroid)。
1. 把计时器设定为25分钟,开始工作,直到响铃,算工作了一个番茄时间。然后在你的本子的工作项后面画一个叉叉表示此项工作已经耗费了一个番茄时间(如果使用软件会自动记录,更为方便)。
在一个番茄时间内,不可以被打断。这意味着等待编译的时候也不可以刷微博,也不要去泡咖啡或者吃东西。如果想到周报还没写,在本子上记上“需要写周 报”然后马上回到工作中。如果有同事打扰你或者接到客户的电话,礼貌地请求他等待20分钟,在本子上记上“需要给王科长回电话”然后马上回到工作中。当然 有时人家也会大喊一声“我等不了了!”,需要你立即紧急处理,那么这个番茄时间就相当于白费了,回到工作中时要重新开始这个番茄时间,就当这个番茄时间从 来没有开始过。响铃之后,你就可以看一下你的本子,处理刚刚记下的事情。如果这些零碎的小事很多,可以开启一个番茄时间集中处理它们。
在 一个番茄时间内,只做一件工作。如果工作了20分钟就完成了,也不要开始下一项工作或者去娱乐,可以回顾一下,检查/测试,总结经验,做一些优化或美化。 如果工作了5分钟就完成了,而且你觉得它本可以在上一个番茄时间里就完成的,并且再去复查一遍也没什么必要,可以将这个番茄时间作废。
2. 休息5分钟。
响铃之后必须立即停止手中的工作,想象你在考试,时间到,马上停笔。即使你觉得在休息的几分钟里就能完成它,也不能继续工作。而且,脑子里也不要再去想任何有关你工作的事情。这其实挺难做到的,我的建议是——离开你的座位——去做运动,或者跟同事聊聊天。
3. 每4个番茄之后休息15分钟。
如果感到疲劳,也可3个番茄就长休一次。或者把休息时间延长,但是不应超过25分钟。

更为详细的内容可以参考网站,电子书英文版中文版(仅32页,值得一读)。番茄工作法由意大利人Francesco Cirillo(弗朗切斯科•齐立罗)发明于1992年。因发明人最开始使用的是番茄形状的计时器而得名。

番茄工作法的好处

健康。 这个世界上有太多好玩的事情让人目不暇接。今晚有NFL季后赛野马vs爱国者耶,另外巴萨的比赛也不能错过,还有NBA快船vs湖人也想看一看。《The Clean Coder》看了一半不能扔下,还想再研究下ios编程……一件接着一件,一直不休息,直到累得实在难受才去床上趴一会儿,要么睡不着要么一睡不起。这种 被动的休息方式对健康极为不利。最好的休息方式是在感到累之前就做短暂的休息。
保持大脑的活力。由于特定 的生化原理,大脑内每个神经元都只能连续工作几分钟,之后就必须休息。连续的娱乐和工作会让大脑处于一种麻木、呆滞的状态。番茄团队经过大量研究发现,为 保持大脑的活力和效率最大化,最佳番茄时间为25~35分钟。我们大多数人连续工作的时间都是1小时或2小时,所以直觉上会怀疑25分钟的工作时间是不是 短了点?会不会刚刚进入状态就被休息打断了?其实,5分钟的休息并不会中断你的思路,反而可以让大脑里的清洁工有时间打扫战场,给脑细胞做做马杀鸡,之后 你会文思泉涌也说不定。
成就感。每个番茄代表一个纯的25分钟的工作时间。每天数一下番茄数就知道自己做了多少有用功,对每项工作耗费了多少时间也会有更具体的感觉。你可以知道自己娱乐了几个番茄,看了几个番茄的书,做了几个番茄的练习,对时间更有掌控感。
安全感。你可以安全地先娱乐一个番茄,然后再看一个番茄的书,不必担心一娱乐起来就停不下。
专心。 娱乐的时候想着工作,工作的时候不忘娱乐,这是我们大多数人的工作方式,也是低效和焦虑的根源。稀里糊涂地就到了下午,到了晚上,计划难以完成,好像做了 很多额外的事情,却又想不起来都干了些什么了。要想摆脱精神涣散的阴影,必须明确地强迫自己“一次只做一件事情,并且必须连续做25分钟”。
张弛有度。根据我的经验,对于一个严重的“再要五分钟综合症”患者,只是自己在心里想着“到九点就休息”是不够的,到时候不是差一点没完成,就是有了新的兴趣点。借助于计时器和“铃声响起立即停止”条款,才能干净利落地根治此症。
不再拖延。拖延症患者大多喜欢追求完美。越是想要做得好,就越容易否定自己,觉得没思路,不够好,信心受挫之下更容易精神涣散。使用番茄法,按下计时器,叮咚,强迫自己没思路也不许干别的,就算干想也要想满25分钟。

娱乐也番茄

书里说空闲的时间就不要用番茄法了。但是我的问题是,因为我觉得刚刚吃饱就工作不太合适,总想先娱乐一会儿。可是一旦娱乐起来就停不下来,所以我想娱乐 也可以使用番茄法。比如“看一个番茄的电影”,或者“看一个番茄的比赛”,之后休息五分钟。这样既有利于健康,也有机会决定之后是继续娱乐一个番茄,还是 工作一个番茄。

番茄你的计划

把番茄法与你的工作计划结合起来也很简单,就是使用番茄数作为工作项的时间单位。首先,你可以通过经验知道你每天平均能做几个番茄的工作。然后,估算每 个工作项需要的番茄数,这样你就能知道明天可以完成哪几个工作项了。每天结束的时候,再回顾一下每个工作项实际使用的番茄数。简单实用,是吧?
小贴士 一个工作项最多5到7个番茄,如果多于这个数,就认为这个任务太过复杂,最好把它分解为几个小任务。如果一个工作项的估值小于一个番茄,把它与其它的小任务组合成一个大任务。
小贴士 你可以在做计划的时候用空心方框“[]”表示一个番茄时间,3个番茄时间就是“[][][]”,把它标记在工作项的后面。实际工作时,每耗费一个番茄时间 就在空心方框里打个叉叉,耗费2个番茄时间是就是“[x][x][]”,耗费4个番茄时间时是“[x][x][x]x”)。

[转载]Asp.net MVC 基于规则的权限设计

mikel阅读(968)

[转载]Asp.net MVC 基于规则的权限设计 – 胡以谦 – 博客园.

上面一篇文章我们简单介绍了一个一级菜单的应用。

在实际的设计中菜单的的信息基本存储在sitemap的xml文件中,菜单还涉及到权限问题。

本章将介绍并举例说明如何设计基于规则的MVC应用程序的安全性。

基于角色的授权

在计算机系统的安全,基于角色的访问控制(RBAC)是一个系统访问限制授权用户的方法。在一个组织内,角色创建的各项工作职能。来执行某些操作的权限分配给特定的角色。
业务上我们必须定义一套针对不同的业务功能的角色体系,例如管理员,数据管理员,普通用户的角色… …

基于规则的访问控制

以规则为基础的授权框架,一般利用XML文档存储简单的规则设置,来控制系统访问权限。(也可以存储在数据库中,读者可以扩展Enterprise Library)

请参见下面的例子。

<rules>
    <add expression="R:Administrator" name="IsAdministrator" />
     <add expression="R:Administrator OR R:DataSteward" name="IsDataSteward" />
     <add expression="R:User OR R:DataSteward OR R:Administrator"  name="IsUser" />
 </rules>

规则“IsAdministrator”会检查当前用户是否有Administrator的角色。 “IsUser”将对角色User, DataSteward或者Administrator都有效。

SecurityHelper

SecurityHelper类利用了Enterprise Library 的默认的AuthorizationRuleProvider,它是我们整个系统的权限核心。主要代码如下。

using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Security;
using Microsoft.Practices.Unity;
using Volvo.CustomerMaster.Infrastructure.Common.Utilities.Entities;
using Volvo.CustomerMaster.Infrastructure.Common.Utilities.Unity;
using Volvo.POS.UserDomain.ServiceLayer;
using Volvo.POS.UserDomain.DomainLayer;
using Volvo.CustomerMaster.Web.Common.Session;
namespace Volvo.CustomerMaster.Web.Common.Security
{
public class SecurityHelper
{
[Dependency]
public static IUserService UserService { get; set; }
/// <summary>
/// Authenticate the user to verify that the user is a logged in user and that the user is approved
/// by the external authorization system.
/// </summary>
/// <returns></returns>
public static bool Authenticate()
{
// Inject implementation of the UserService through DI
if (UserService == null)
{
UserService = Container.Resolve<UserService>();
}
string userName = GetWindowsVcnUserName();
// Get user from external authorization system
GenericUser user = UserService.GetUser(userName);
if (user == null)
{
return false;
}
// Set session
SessionWrapper.CurrentUser = user;
return true;
}
/// <summary>
/// Returns true if the user contain roles that is valid for selected rule
/// </summary>
/// <param name="rule"></param>
/// <returns></returns>
public static bool Authorized(string rule)
{
try
{
IList<string> rules = new List<string> { rule };
return Authorized(rules);
}
catch
{
return false;
}
}
/// <summary>
/// Returns true if the user contain roles that is valid for selected rules
/// </summary>
/// <param name="rules"></param>
/// <returns></returns>
public static bool Authorized(IList<string> rules)
{
// If user is not defined, try to authenticate it
if (SessionWrapper.CurrentUser == null)
{
if (!Authenticate())
{
return false;
}
}
// Get authorization provider from Entlib
IAuthorizationProvider auth = AuthorizationFactory.GetAuthorizationProvider("RulesProvider");
if (rules.Count > 0 && SessionWrapper.CurrentUser.Principal != null)
{
foreach (string rule in rules)
{
// Authorize user (with its roles) agains the rule
if (!auth.Authorize(SessionWrapper.CurrentUser.Principal, rule))
{
return false;
}
}
}
else
{
return false;
}
return true;
}
private static string GetWindowsVcnUserName()
{
// Get windows user
System.Security.Principal.WindowsIdentity loggedInUser =
System.Security.Principal.WindowsIdentity.GetCurrent();
if (loggedInUser != null)
{
string username = loggedInUser.Name;
username = username.Substring(username.IndexOf('\\') + 1);
username = username.ToUpper();
return username;
}
return null;
}
}
}

其中对当前用户检查某个规则的有效性代码如下。

IAuthorizationProvider auth = AuthorizationFactory.GetAuthorizationProvider("RulesProvider");
if (rules.Count > 0 && SessionWrapper.CurrentUser.Principal != null)
{
foreach (string rule in rules)
{
// Authorize user (with its roles) agains the rule
if (!auth.Authorize(SessionWrapper.CurrentUser.Principal, rule))
{
return false;
}
}
}
else
{
return false;
}
return true;

菜单的访问控制

在Web.sitemap文件中我们对每个节点增加一个属性,AuthorizationRule这样菜单和用户角色就关联起来了。

<?xml version="1.0" encoding="utf-8" ?>
<siteMap enableLocalization="true">
  <siteMapNode title="Menu">
    <siteMapNode controller="Home" title="Home"    action="Index" resourceKey="Tab_Home" AuthorizationRule="IsUser"/>
    <siteMapNode controller="Customer" title="Manage Customers"    action="Index" resourceKey="Tab_ManageCustomers" AuthorizationRule="IsDataSteward"/>
    <siteMapNode title="Switching Brands" resourceKey="Tab_SwitchingBrands" AuthorizationRule="IsUser">
      <siteMapNode title="Violin" controller="Home" action="SetTheme/Violin"  AuthorizationRule="IsUser"/>
      <siteMapNode title="Mack" controller="Home" action="SetTheme/Mack" AuthorizationRule="IsUser"/>
      <siteMapNode title="Mack Dual" controller="Home" action="SetTheme/MackDual" AuthorizationRule="IsUser"/>
      <siteMapNode title="Renault" controller="Home" action="SetTheme/Renault"  AuthorizationRule="IsUser"/>
      <siteMapNode title="Volvo BA" controller="Home" action="SetTheme/VolvoBA" AuthorizationRule="IsUser"/>
      <siteMapNode title="Volvo Group" controller="Home" action="SetTheme/VolvoGroup" AuthorizationRule="IsUser"/>
    </siteMapNode>
  </siteMapNode>
</siteMap>

菜单的规则如何、什么时候被加载呢?在渲染菜单的SiteMapBinding.cshtml文件中,我们的代码如下。(示例利用了Telerik for ASP.NET MVC控件)

@using Volvo.CustomerMaster.Web.Common.Security
@using Volvo.CustomerMaster.Web

 @{ Html.Telerik().Menu()
        .Name("Menu")
        .BindTo("Web",(item, node) =>{
             if (node.Attributes["resourceKey"] !=null)
                item.Text = UI_Resources.ResourceManager.GetString(node.Attributes["resourceKey"] as string) ?? item.Text;
            if(node.Attributes["imageurl"] != null)
            item.ImageUrl = node.Attributes["imageurl"].ToString();
            item.Visible = SecurityHelper.Authorized(node.Attributes["AuthorizationRule"].ToString());
        })
         .Effects(fx =>
          fx.Toggle()
          .OpenDuration(200)
          .CloseDuration(200))
       .Render();
 }

其中item.Visible=SecurityHelper.Authorized(node.Attributes[“AuthorizationRule”].ToString());这行代码就决定了菜单的可见性由我们定义的规则控制。

UI元素访问控制

利用同样原理,按钮的enable/disable也可以基于规则来控制。我们首先构造一个类 (HtmlHelper)用于在页面上显示按钮。

ButtonHelper

以下核心代码将权限规则和按钮的显示关联。

Button

在页面中,我们如何利用ButtonHelper呢?下面的例子利用Telerik来显示一个Grid,在Grid的头上我么将显示edit, add, delete 按钮。

按钮的生成就利用了我么的ButtonHelper类。它提供了一些扩展方法。

@(Html.Telerik().Grid<Customer>()
         .Name("CustomerGrid")
         .EnableCustomBinding(true)
         .DataBinding(bind => bind.Ajax().Select("ListCustomerAjax", "Customer"))
         .ToolBar(toolBar => toolBar.Template
          (
             @Html.Button("toolbarEditRow", UI_Resources.ListCustomer_EditCustomerButton, 
             ButtonHelper.SetButtonDisability("toolbarEditRow", "IsAdministrator"), 
             new { title = UI_Resources.ListCustomer_EditCustomerButtonTooltip, @class = "icon edit" })
             +"<span >&nbsp;</span>"+
             @Html.Button("toolbarAddRow", UI_Resources.ListCustomer_AddNewCustomerButton, ButtonHelper.SetButtonDisability("toolbarAddRow", "IsAdministrator"), new { title = UI_Resources.ListCustomer_AddNewCustomerButtonTooltip, @class = "icon add" })
             +"<span >&nbsp;</span>"+
             @Html.Button("toolbarDeleteRow", UI_Resources.ListCustomer_DeleteCustomerButton, ButtonHelper.SetButtonDisability("toolbarDeleteRow", "IsAdministrator"), new { title = UI_Resources.ListCustomer_DeleteCustomerButtonTooltip, @class = "icon delete" })
          ))
         .Columns(columns =>
                    {
                        columns.Bound(o => o.Number).Title("Number").Width(40);
                        columns.Bound(o => o.Name).Title("Name").Width(100);
                        columns.Bound(o => o.Address).Title("Address").Width(100);
                    }
                 )
        .ClientEvents(x => x.OnLoad("CustomerGrid_OnLoad"))
        .Selectable()
        .Pageable(paging => paging.PageSize(10).Total(Model.CustomerCount))
        .Sortable())

显示按钮的时候,我们调用了ButtonHelper.SetButtonDisability来控制按钮的enable/disable状态,我们也可以通过它来控制显示、不显示按钮。

MVC Controller类的访问控制

有些用户可能会直接在浏览器中输入URL来绕过菜单的权限控制,我们必须在MVC的Controller级别加上我们的基于规则的权限管理。

我们增加一个新的类RuleAuthorizeAttribute,它继承于System.Web.Mvc.AuthorizeAttribute

View Code

代码很简单,它也利用了我们前面提到的SecurityHelper类的功能。

我们把这个属性设置的示例程序中的CustomerController类中。

    [HandleError]
    [RuleAuthorize(Allow="IsDataSteward")]
    public class CustomerController : BaseController
    {
        public ICustomerService CustomerService { get; set; }

        public CustomerController(ICustomerService customerService)
        {
            CustomerService = customerService;  
            
        }

假设我们登录的用户没有DataSteward或Administrator角色,但是他尝试直接在浏览器里面输入URL:http://localhost:2967/Customer。

新增的Filter控制了直接URL的权限管理。

按钮显示的控制

———————————————————————-
示例代码.

http://files.cnblogs.com/huyq2002/Sample.zip

运行程序您需要生成数据库CustomerMaster,运行CustomerMaster.SQL,同时修改NHibernate.config中的connection.connection_string

系统适用于域认证,也很容易扩展到其他认证方式,如form认证等

通过改变代码中的UserService的GetUser方法可以模拟不同的角色来使用系统

// 2. Get roles defined for the user
            if (userName.Equals("v0cn174", StringComparison.CurrentCultureIgnoreCase))
            {
                //user.AddRole(new UserRole(UserRoleConstants.Administrator));
                user.AddRole(new UserRole(UserRoleConstants.DataSteward));
                //user.AddRole(new UserRole(UserRoleConstants.User));
            }
            else
            {
                // All users are superusers in this mock
                //user.AddRole(new UserRole(UserRoleConstants.Administrator));
                //user.AddRole(new UserRole(UserRoleConstants.DataSteward));
                user.AddRole(new UserRole(UserRoleConstants.User));
            }
            return user;

[转载]SqlServer批量清理指定数据库中所有数据

mikel阅读(1004)

[转载][推荐] (SqlServer)批量清理指定数据库中所有数据 – 海南.胡勇 – 博客园.

——通过知识共享树立个人品牌。

在实际应用中,当我们准备把一个项目移交至客户手中使用时,我们需要把库中所有表先前的测试数据清空,以给客户一个干净的数据库,如果涉及的表很多,要一 一的清空,不仅花费时间,还容易出错以及漏删,在这儿我提供了一个方法,可快捷有效的清空指定数据库所有表的数据。仅供参考,欢迎交流不同意见。

Remove all data from a database

SET NOCOUNT ON
Tables to ignore
DECLARE @IgnoreTables
TABLE (TableName varchar(512))
INSERT INTO @IgnoreTables (TableName) VALUES (sysdiagrams)
DECLARE @AllRelationships
TABLE (ForeignKey varchar(512)
,TableName varchar(512)
,ColumnName varchar(512)
,ReferenceTableName varchar(512)
,ReferenceColumnName varchar(512)
,DeleteRule varchar(512))
INSERT INTO @AllRelationships
SELECT f.name AS ForeignKey,
OBJECT_NAME(f.parent_object_id) AS TableName,
COL_NAME(fc.parent_object_id,
fc.parent_column_id) AS ColumnName,
OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
COL_NAME(fc.referenced_object_id,
fc.referenced_column_id) AS ReferenceColumnName,
delete_referential_action_desc as DeleteRule
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id

DECLARE @TableOwner varchar(512)
DECLARE @TableName varchar(512)
DECLARE @ForeignKey varchar(512)
DECLARE @ColumnName varchar(512)
DECLARE @ReferenceTableName varchar(512)
DECLARE @ReferenceColumnName varchar(512)
DECLARE @DeleteRule varchar(512)

PRINT(Loop through all tables and switch all constraints to have a delete rule of CASCADE)
DECLARE DataBaseTables0
CURSOR FOR
SELECT SCHEMA_NAME(t.schema_id) AS schema_name, t.name AS table_name
FROM sys.tables AS t;

OPEN DataBaseTables0;

FETCH NEXT FROM DataBaseTables0
INTO @TableOwner,@TableName;

WHILE @@FETCH_STATUS = 0
BEGIN
IF (NOT EXISTS(SELECT TOP 1 1 FROM @IgnoreTables WHERE TableName = @TableName))
BEGIN
PRINT [+@TableOwner+].[ + @TableName + ];

DECLARE DataBaseTableRelationships CURSOR FOR
SELECT ForeignKey, ColumnName, ReferenceTableName, ReferenceColumnName
FROM @AllRelationships
WHERE TableName = @TableName

OPEN DataBaseTableRelationships;
FETCH NEXT FROM DataBaseTableRelationships INTO @ForeignKey@ColumnName@ReferenceTableName@ReferenceColumnName;

IF @@FETCH_STATUS <> 0
PRINT =====> No Relationships ;

WHILE @@FETCH_STATUS = 0
BEGIN
PRINT =====> switching delete rule on + @ForeignKey + to CASCADE;
BEGIN TRANSACTION
BEGIN TRY
EXEC(

ALTER TABLE [+@TableOwner+].[ + @TableName + ]
DROP CONSTRAINT
+@ForeignKey+;

ALTER TABLE [+@TableOwner+].[ + @TableName + ] ADD CONSTRAINT
+@ForeignKey+ FOREIGN KEY
(
+@ColumnName+
) REFERENCES
+@ReferenceTableName+
(
+@ReferenceColumnName+
) ON DELETE CASCADE;
);
COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT =====> cant switch + @ForeignKey + to CASCADE, – +
CAST(ERROR_NUMBER() AS VARCHAR+ + ERROR_MESSAGE();
ROLLBACK TRANSACTION
END CATCH;

FETCH NEXT FROM DataBaseTableRelationships INTO @ForeignKey@ColumnName@ReferenceTableName@ReferenceColumnName;
END;

CLOSE DataBaseTableRelationships;
DEALLOCATE DataBaseTableRelationships;

END
PRINT ;
PRINT ;

FETCH NEXT FROM DataBaseTables0
INTO @TableOwner,@TableName;
END
CLOSE DataBaseTables0;
DEALLOCATE DataBaseTables0;

PRINT(Loop though each table and DELETE All data from the table)

DECLARE DataBaseTables1 CURSOR FOR
SELECT SCHEMA_NAME(t.schema_id) AS schema_name, t.name AS table_name
FROM sys.tables AS t;

OPEN DataBaseTables1;

FETCH NEXT FROM DataBaseTables1
INTO @TableOwner,@TableName;

WHILE @@FETCH_STATUS = 0
BEGIN
IF (NOT EXISTS(SELECT TOP 1 1 FROM @IgnoreTables WHERE TableName = @TableName))
BEGIN
PRINT [+@TableOwner+].[ + @TableName + ];
PRINT =====> deleting data from [+@TableOwner+].[ + @TableName + ];
BEGIN TRY
EXEC(
DELETE FROM [
+@TableOwner+].[ + @TableName + ]
DBCC CHECKIDENT ([
+ @TableName + ], RESEED, 0)
);
END TRY
BEGIN CATCH
PRINT =====> cant FROM [+@TableOwner+].[ + @TableName + ], – +
CAST(ERROR_NUMBER() AS VARCHAR+ + ERROR_MESSAGE();
END CATCH;
END

PRINT ;
PRINT ;

FETCH NEXT FROM DataBaseTables1
INTO @TableOwner,@TableName;
END
CLOSE DataBaseTables1;
DEALLOCATE DataBaseTables1;

PRINT(Loop through all tables and switch all constraints to have a delete rule they had at the beggining of the task)

DECLARE DataBaseTables2 CURSOR FOR
SELECT SCHEMA_NAME(t.schema_id) AS schema_name, t.name AS table_name
FROM sys.tables AS t;
OPEN DataBaseTables2;

FETCH NEXT FROM DataBaseTables2
INTO @TableOwner,@TableName;

WHILE @@FETCH_STATUS = 0
BEGIN

IF (NOT EXISTS(SELECT TOP 1 1 FROM @IgnoreTables WHERE TableName = @TableName))
BEGIN
PRINT [+@TableOwner+].[ + @TableName + ];

DECLARE DataBaseTableRelationships CURSOR FOR
SELECT ForeignKey, ColumnName, ReferenceTableName, ReferenceColumnName, DeleteRule
FROM @AllRelationships
WHERE TableName = @TableName

OPEN DataBaseTableRelationships;
FETCH NEXT FROM DataBaseTableRelationships INTO @ForeignKey@ColumnName@ReferenceTableName@ReferenceColumnName@DeleteRule;

IF @@FETCH_STATUS <> 0
PRINT =====> No Relationships ;

WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @switchBackTo varchar(50=
CASE
WHEN @DeleteRule = NO_ACTION THEN NO ACTION
WHEN @DeleteRule = CASCADE THEN CASCADE
WHEN @DeleteRule = SET_NULL THEN SET NULL
WHEN @DeleteRule = SET_DEFAULT THEN SET DEFAULT
END

PRINT =====> switching delete rule on + @ForeignKey + to + @switchBackTo;

BEGIN TRANSACTION
BEGIN TRY
EXEC(

ALTER TABLE [+@TableOwner+].[ + @TableName + ]
DROP CONSTRAINT
+@ForeignKey+;

ALTER TABLE [+@TableOwner+].[ + @TableName + ] ADD CONSTRAINT
+@ForeignKey+ FOREIGN KEY
(
+@ColumnName+
) REFERENCES
+@ReferenceTableName+
(
+@ReferenceColumnName+
) ON DELETE
+@switchBackTo+
);

COMMIT TRANSACTION
END TRY
BEGIN CATCH
PRINT =====> cant change +@ForeignKey + back to + @switchBackTo +, – +
CAST(ERROR_NUMBER() AS VARCHAR+ + ERROR_MESSAGE();
ROLLBACK TRANSACTION
END CATCH;

FETCH NEXT FROM DataBaseTableRelationships
INTO @ForeignKey@ColumnName@ReferenceTableName@ReferenceColumnName@DeleteRule;
END;

CLOSE DataBaseTableRelationships;
DEALLOCATE DataBaseTableRelationships;

END
PRINT ;
PRINT ;

FETCH NEXT FROM DataBaseTables2
INTO @TableOwner,@TableName;
END
CLOSE DataBaseTables2;

DEALLOCATE DataBaseTables2;

© 2011 EricHu

原创作品,转贴请注明作者和出处,留此信息。

[转载]SqlServer分离所有用户数据库

mikel阅读(1028)

[转载][推荐] (SqlServer)分离所有用户数据库 – 海南.胡勇 – 博客园.

在实际应用中,有时我们需要一次性分离所有用户数据库,下面给出代码,供大家参考。

USE [master]
GO
IF EXISTSSELECT *
FROM sys.objects
WHERE [object_id] = OBJECT_ID(N[dbo].[spDetachAllUserDatabases])
AND type IN ( NP, NPC ) )
DROP PROCEDURE [dbo].[spDetachAllUserDatabases]
GO

CREATE PROCEDURE [dbo].[spDetachAllUserDatabases]
AS
BEGIN
Declare Variables
DECLARE @DatabaseName VARCHAR(100)
DECLARE @MinDatabaseID INT
DECLARE @MaxDatabaseID INT
DECLARE @SQL VARCHAR(4000)
Check for temporary table and drop it if it exists
IF OBJECT_ID(tempDB.dbo.#DatabaseIS NOT NULL
DROP TABLE [#Database];

Create temporary table
CREATE TABLE #Database
(
ID INT IDENTITY(11),
DatabaseName VARCHAR(100)
)

Check for existing user databases
IF EXISTSSELECT name
FROM sys.databases
WHERE database_id > 4
AND name NOT INSQLDBAReportServer,
ReportServerTempDB,
distribution ) )
BEGIN
Insert all database names into a temporary table
INSERT INTO #Database ( DatabaseName )
SELECT name
FROM sys.databases
WHERE database_id > 4
AND name NOT INSQLDBAReportServer,
ReportServerTempDB,
distribution )

Set Variables for the detach database loop
SELECT @MinDatabaseID = MIN(ID),
@MaxDatabaseID = MAX(ID)
FROM #Database

Begin loop to detach databases
WHILE @MinDatabaseID <= @MaxDatabaseID
BEGIN

Get DatabaseName
SELECT @DatabaseName = DatabaseName
FROM #Database
WHERE ID = @MinDatabaseID

Build Detach Database Command
SET @SQL = EXEC sp_detach_db + ”” + @DatabaseName
+ ”” + ;

Try Catch block to execute SQL and handle errors
BEGIN TRY

Detach Database
EXEC@SQL
)
PRINT Detached + @DatabaseName
END TRY
BEGIN CATCH
SELECT @DatabaseName,
message_id,
severity,
[text],
@SQL
FROM sys.messages
WHERE message_id = @@ERROR
AND language_id = 1033 British English
END CATCH

Get the next DatabaseName ID
SET @MinDatabaseID = @MinDatabaseID + 1

End Loop
END
END
END

GO

© 2011 EricHu

原创作品,转贴请注明作者和出处,留此信息。

————————————————

[转载]存储过程和触发器笔记

mikel阅读(959)

[转载]存储过程和触发器笔记 – snidget – 博客园.

1. 存储过程和触发器是什么?

存储过程不能可以显著提高系统的执行速度,还能提高效率确保一致性.

1.1存储过程:一种数据库对象,将负责不同功能的语句分类放置起来,以便能反复使用.

1.2特点:


1.3存储过程的分类

分为五类,系统存储过程,用户定义存储过程,临时存储过程,远程存储过程,扩展存储过程.

临时存储过程又分为本地临时存储过程,全局临时存储过程.

分类

说明

系统存储过程 存储在master中,以sp开头,调用时不必加库名,如果参数是保留字或者数据库对象,用单引号包围.
用户定义存储过程 用户为完成一定的功能定义在数据库中存储过程.
临时存储过程 本地临时:#开头,放在tempdb中,连接断开之后自动删除,本库使用.
全局临时:##开头,放在tempdb中,连接断开后使用完之后自动删除,本连接的所有库可以使用.注意命名.
远程存储过程 位于远程服务器上的存储过程.通过分布式查询和execute执行.
扩展存储过程 外部程序写的存储过程,xp开头,动态链接库形式存在,也放在master

1.4存储过程优点


1.5存储过程和视图的比较


1.6创建存储过程

格式:


例子:

create procedure ShowAllStudent

as

begin

select * from students

end

1.7执行存储过程

格式:exec procedure_name

例子:exec  ShowAllStudent

1.8带输入参数存储过程

格式:


例子:

create procedure SelectStudentByStu_no

@sno char(12)=‘200501020319’

as

begin

select * from students where stu_no=@sno

end

1.9执行带输入参数的存储过程

格式:


例子:

exec SelectStudentByStu_no

@sno=‘200501030218’

当参数比较少的时候,可以按照位置传递参数.

例子:

exec SelectStudentByStu_no ‘200501030218’

1.10带输出参数的存储过程

格式:


例子:

create procedure GetStudentCountByStu_sex

@sex char(2)=,

@count int=0 output

as

begin

set

@count=(select count(*) from students where stu_gender=@sex)

print @count

end

执行带输出参数的存储过程

例子:

declare @sex char(2) ,@count int

set @sex=

exec GetStudentCountByStu_sex @sex,@count

1.11 删除存储过程

Drop procedure GetPostsByBlogId

1.12 更新存储过程

alter procedure GetPostsByBlogId

(@blogid varchar(50))

As

Select top 5* from Posts where BlogId=@blogid

1.13 重命名存储过程

对象浏览器中修改.

1.14 重新编译存储过程

有三种方法:

1)创建的时候使用with Recompile 语句.

2)在执行过程中设定重新编译

3)调用系统存储过程重新编译

2. 触发器

基本表被在修改的时候通过事件触发而执行的存储过程.

作用是保证了由主键和外键所不能保证的参照完整性和数据完整性.

2.1触发器的优点

触发器可以包含复杂的处理逻辑,主要用来保持低级的数据完整性.优点如下.



2.2创建触发器

删除触发器:


例子:

create trigger dropStudent

on students

for delete

as

print 成功删除一条数据.’

更新触发器:

create trigger updateStudentName

on students

for update

as

if update (stu_name)

begin

print 不能更新主键,学生号码.’

rollback transaction

end

update students set

stu_name=五哥

where stu_no=‘200501020319’


删除触发器:略

查看触发器:

1) 使用系统的存储过程查看: exec sp_helptrigger students


使用系统表: select name from sysobjects where type=‘TR’


2) 管理器查看 略

删除触发器: drop trigger updateStudent

修改触发器:


重命名: sp_rename dropStudent,deleteStudentItem

启动和停止触发器:

alter table students enable trigger all

[转载]强力推荐几种多媒体播放器方案(jQuery、Flash、HTML5)

mikel阅读(912)

[转载]强力推荐几种多媒体播放器方案(jQuery、Flash、HTML5) – sxwgf – 博客园.

记得以前我们想要在网页中插入一段音频或者视频时,首先,baidu或者google一下,目的是要找那段又长又臭的object和embed代 码,然后再把它们贴到网页中,然后还发现不同的浏览器写法还不一样,有些支持object不支持embed,有些却是反一下的,到头来还是调用系统的 windows media player,没装这个的就白搭了,弄得蛋疼。后来,我们可能渐渐地去用Flash播放器了,毕竟大部分浏览器对Flash的支持还是比较统一的,而且做 出来的播放器也比较好看,各种个性化的皮肤定制,让人不免兴奋一把。现在这段时间又出了HTML5,而且来势汹汹,在HTML5平台上也涌现出了好多多媒 体播放器。下面我就给大家推荐几款多媒体播放器。

先看JS版本的,主要用JQuery

jQuery Media Plugin(推荐)中文解说

JQuery Media Plugin是一款基于JQuery的网页媒体播放器插件,它支持大部分的网络多媒体播放器和多媒体格式,比如:Flash, Windows Media Player, Real Player, Quicktime, MP3,Silverlight, PDF。他会自动根据不同的平台生成相应的object或者embed代码,所以你就不要操心平台兼容了。

image

jmp3

这个是单独的mp3播放器,也是jQuery的,当然最终生成的是flash

image

再来看几个纯Flash的播放器

一款带有播放列表的Flash播放器,有示例和源码(推荐)

可配置的播放列表(用XML配置),并且播放列表带有缩略图预览;fla源代码公开,你可以根据自己的需要修改源代码;支持flv格式的视频。

image

flowplayer

FlowPlayer 是一个用Flash开发的在Web上的视频播放器,可以很容易将它集成在任何的网页上。支持HTTP以及流媒体传输。

image

最后就再强力推荐几款HTML5播放器了,未来的趋势是HTML5嘛

jPlayer(推荐)中文解说

其实说实在的,jPlayer可以说是一个媒体播放器框架了,因为有一些播放器就是基于jPlayer来写的。它不仅可以播放音频,也能播放视频,而且还有很不错的皮肤哦,相信这也是HTML5的魅力。强力推荐啊!

image

VideoJS 中文解说

  1. 开源,我们可以利用VideoJS的源码将它应用到自己的程序中,并且可以修改其源码,但要遵循开源协议。
  2. 轻量级,没有使用图片。
  3. 完全可以用CSS定制播放器的外观。
  4. 不依赖其他脚本库。
  5. 使用非常简单。
  6. 不仅使用简单,扩展也非常简单。
  7. 在各个浏览器之间有统一的外观。
  8. 支持全屏播放。
  9. 可以控制音量。
  10. 遇到不支持HTML5的浏览器可以转换成FLASH来播放。

image

ZEN Audio Player 中文解说

这个就是上面我说的jPlayer的一个变种,一个富有个性化的环形音频播放器,chrome下效果最好

image

open standard media

  1. 开源,免费
  2. 使用目前最流行的jQuery框架写的
  3. 动态HTML5内容交付
  4. 支持多种常用的音频和视频格式
  5. 遇到非标准媒体自动使用flash来播放
  6. 使用ThemeRoller可以方便地定制皮肤
  7. 与Vimeo和YouTube无缝集成
  8. 支持播放列表
  9. 播放器之间实现通信

image

[转载]ASP.NET MVC框架VICI 测试的便利性

mikel阅读(1028)

[转载]ASP.NET开源MVC框架VICI 测试的便利性 – (张超) – 博客园.

平滑的学习曲线是微软技术的最大特征,在ASP.NET上,从一开始我们什么都不会,到慢慢的基本什么都会了,下一个值得向我这样的菜鸟突破的是什么呢?是海量并发?海量数据?高可扩展性?高度安全性?高度稳定性?

开始我觉得可能可扩展性比较重要,经过一年满负荷的工作,我现反倒觉得他们都是在扯淡,除了给脸上贴金之外没什么用,应用程序的可测试性应该是学下一道门 槛,不可能测试的项目会令项目组进入开发组开发-》测试组手工测试出现BUG=》开发组开发改bug恶性的循环,最要命的是不可测试的程序无法让测试组 使用绝大多数测试工具,造成bug潜伏;

另外,可测试性也是个人学习 《重构》、《单元测试》、《性能优化》等的前提,自己写的东西不可测试,这些东西看了一点用也没用,书中每100个字就要4个单词是测试- -;这些东西都需要用测试来检验所做的修改是否正确。可测试性应该是ASP.NET学习的第二道门坎(PS 第一道门坎是 ViewState

以前关于vici的文章 http://www.cnblogs.com/qqloving/category/292931.html

开源ASP.NET MVC (官网地址:http://viciproject.com/index

有人可能说 微软搞那个开源的mvc已经很不错了,可是在可测试方面和vici比起来 还差的很远

官网的案例下载包含测试代码:http://viciproject.com/wiki/Projects/Mvc/Download

vicimvc 为以下几个方面提供了非常简单的测试方法

1 没有登录的用户进入授权页面时,页面应跳转到登陆页面

2 检查应用程序是否正确重定向到登录页

3 填写用户名和密码 登录(模拟登录)

4 检查当前页面是否包含我们所需要的html标签

测试部分的代码

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

using Vici.Mvc;

using NUnit.Framework;
using Demo.ViciMvc;

namespace UnitTest.Demo.ViciMVC
{
    /// <summary>
    /// Summary description for UnitTest2
    /// </summary>
    [TestFixture]
    public class UnitTest2
    {
        /// <summary>
        /// 测试没有登录的用户访问main 页面 则需要跳转到login登陆页
        /// 
        /// </summary>
        [Test]
        public void TestUnauthorizedAccess()
        {
            OfflineWebSession webSession = CreateWebSession();

            // Navigate to a page which requires login
            webSession.PageGet("/main");

            // Check if we were redirected to the login page
            Assert.AreEqual("/login", webSession.CurrentPage);
        }

        /// <summary>
        /// 测试用户登录
        /// 什么编写测试脚本 录制测试脚本 都弱爆了!!!!
        /// </summary>
        [Test]
        public void TestLogin()
        {
            OfflineWebSession webSession = CreateWebSession();

            // Navigate to the login page
            webSession.PageGet("/login");

            // Enter login, password and click on the "btnLogin" button
            webSession.PostData["login"] = "demo";
            webSession.PostData["password"] = "promesh";
            webSession.PushButton("btnLogin");

            // Post form
            string html = string.Empty;
           html= webSession.PagePost(webSession.CurrentPage);

            // Check if we were redirected to the main page
            Assert.AreEqual("/main", webSession.CurrentPage,html);

            // Load the user we logged in as
            User user = DataService.FindUser("demo", "promesh");

            // Check if the session contains the correct user
            Assert.AreEqual(Application.Session.User.UserID, user.UserID, "Wrong user logged in???");
        }

        private string Login(OfflineWebSession webSession)
        {
            webSession.PageGet("/login");

            // Enter login, password and click on the "btnLogin" button
            webSession.PostData["login"] = "demo";
            webSession.PostData["password"] = "promesh";
            webSession.PushButton("btnLogin");
           return  webSession.PagePost(webSession.CurrentPage);

            // Post form
            //return webSession.CurrentPage;

        }

        [Test]
        public void TestEditPage()
        {
            OfflineWebSession webSession = CreateWebSession();

            //这个是当前页面的html 
            string html = Login(webSession);
            Assert.IsTrue(html.Contains("/employee/edit/1"), html);

        }

        private static OfflineWebSession CreateWebSession()
        {
            OfflineWebSession webSession = new OfflineWebSession(System.IO.Path.GetFullPath(@"..\..\..\Demo.ViciMVC"));

            webSession.FollowRedirects = true;

            return webSession;
        }
    }
}

vici 能够进行有效的测试 是他对session进行了自己特殊的实现 自己实现了一个OfflineWebSession, 专门用来做测试

CreateWebSession 用来创建一个测试用的session,

        private static OfflineWebSession CreateWebSession()
        {
            OfflineWebSession webSession = new OfflineWebSession(System.IO.Path.GetFullPath(@"..\..\..\Demo.ViciMVC"));

            webSession.FollowRedirects = true;

            return webSession;
        }

vici 的测试代码有一些配置项

<configuration>

  <appSettings>
    <add key="Mvc.ApplicationClass" value="Demo.ViciMvc.Application, Demo.ViciMVC" /> 框架的dll
    <add key="Mvc.TemplatePath" value="Content/templates" /> 模板的路径
  </appSettings>

</configuration>

@”..\..\..\Demo.ViciMVC” 为要测试项目的地址

一提到要登录,就要输入用户名呢密码或者录制测试脚本,vici的实现超级简单

 /// <summary>
        /// 测试用户登录
        /// 什么编写测试脚本 录制测试脚本 都弱爆了!!!!
        /// </summary>
        [Test]
        public void TestLogin()
        {
            OfflineWebSession webSession = CreateWebSession();

            // Navigate to the login page
            webSession.PageGet("/login");

            // Enter login, password and click on the "btnLogin" button
            webSession.PostData["login"] = "demo";
            webSession.PostData["password"] = "promesh";
            webSession.PushButton("btnLogin");

            // Post form
            string html = string.Empty;
           html= webSession.PagePost(webSession.CurrentPage);

            // Check if we were redirected to the main page
            Assert.AreEqual("/main", webSession.CurrentPage,html);

            // Load the user we logged in as
            User user = DataService.FindUser("demo", "promesh");

            // Check if the session contains the correct user
            Assert.AreEqual(Application.Session.User.UserID, user.UserID, "Wrong user logged in???");
        }

webSession.PageGet(“/login”); 和webSession.PagePost(webSession.CurrentPage); 返回一个字符串是当前请求的url 的页面的html,可以通过这个字符串是否包含我们需要的数据来 进行一些测试

例如 实例代码

        [Test]
        public void TestEditPage()
        {
            OfflineWebSession webSession = CreateWebSession();

            string html = Login(webSession);
            Assert.IsTrue(html.Contains("/employee/edit/1"), html);

        }

vici 的测试代码 无论是 通过

NUnit.Framework 还是 微软自带的测试框架 都可以测试的

所以 vic 果断 NX