[转载]浅析Web数据存储-Cookie、UserData、SessionStorage、WebSqlDatabase

mikel阅读(1203)

[转载]浅析Web数据存储-Cookie、UserData、SessionStorage、WebSqlDatabase – 葡萄城控件技术团队博客 – 博客园.

Cookie

它是标准的客户端浏览器状态保存方式,可能在浏览器诞生不久就有Cookie了,为什么需要Cookie 这个东东?由于HTTP协议没有状态,所以需要一个标志/存储来记录客户浏览器当前的状态,保证客户浏览器和服务器通讯时可以知道客户浏览器当前的状态。 Cookie就是记录这个状态的容器,Cookie在每次请求的时候都被带回到服务器,从而保证了Server可以知道浏览器当前的状态,由于 Cookie会被带回到Server,所以Cookie的内容不能存太多,最多不能超过4K,4K 限制的介绍 http://ec.europa.eu/ipg/standards/cookies/index_en.htm
其中一段内容为:

A browser is only required to store up to 300 cookies overall and maintain only the last 20 from each domain. The maximum size of a cookie is 4K of disk space.

但是在一些场景下可能需要存储超过4K或者更多的数据,但是这些数据不用在每次请求的时候被带回到服务器,只要能在客户的浏览器上保存住,并且可以 方便的被JavaScript读写就可以了,这种需求尤为在中大型RIA的应用场景下更加的迫切,部分数据放在客户浏览器,节约带宽,提高浏览速度。 HTML5标准已经替我们想到了满足这种需求的方案:sessionStorage , webSQLDatabase, 微软的IE 有 userData 方案。
userData
微软对USERDATA的介绍: http://msdn2.microsoft.com/en-us/library/ms531424(VS.85).aspx
其中一段内容为:

Security Alert:For security reasons, a UserData store is available only in the same directory and with the same protocol used to persist the store.
Security Alert:Using this behavior incorrectly can compromise the security of your application. Data in a UserData store is not encrypted and therefore not secure. Any application that has access to the drive where UserData is saved has access to the data. Therefore, it is recommended that you not persist sensitive data like credit card numbers. For more information, see Security Considerations: DHTML and Default Behaviors.
……
The userData behavior persists data across sessions, using one UserData store for each object. The UserData store is persisted in the cache using the save and load methods. Once the UserData store has been saved, it can be reloaded even if Microsoft Internet Explorer has been closed and reopened.
Setting the userData behavior class on the html, head, title, or style object causes an error when the save or load method is called.

userData可以在同目录同协议下相互访问,长期存储在客户机器上。最大存储空间也增大了很多。userData需要绑定到一个Dom元素上使 用。在userData的method中有removeAttribute方法。经过测试代码发现removeAttribute方法好像不是很管用,需 要使用像cookie过期的方式,才可以彻底的删除一个userData Attribute。
http://www.itwen.com/04web/11skill/skill20060918/60588.html 中介绍说userData存储在X:\Documents and Settings\当前用户\UserData\ 目录下。具体细节MS在userData说明文档中没有具体说明。
sessionStorage
HTML5 标准对 sessionStorage的介绍: http://www.whatwg.org/specs/web-apps/current-work/
其中对 sessionStorage 的介绍:

This specification introduces two related mechanisms, similar to HTTP session cookies [RFC2965], for storing structured data on the client side.
The first is designed for scenarios where the user is carrying out a single transaction, but could be carrying out multiple transactions in different windows at the same time.
Cookies dont really handle this case well. For example, a user could be buying plane tickets in two different windows, using the same site. If the site used cookies to keep track of which ticket the user was buying, then as the user clicked from page to page in both windows, the ticket currently being purchased would “leak” from one window to the other, potentially causing the user to buy two tickets for the same flight without really noticing.
To address this, this specification introduces the sessionStorage DOM attribute. Sites can add data to the session storage, and it will be accessible to any page from that origin opened in that window.

Html5 sessionStorage Demo: http://html5demos.com/storage
下面是根据 http://www.blogjava.net/emu/archive/2006/10/04/73385.html 中提到的IE FF 兼容userData的测试代码:

01 function isIE() {
02 return !!document.all;
03 }
04
05 function initUserData() {
06 if (isIE()) document.documentElement.addBehavior("#default#userdata");
07 }
08
09 function saveUserData(key, value) {
10 var ex;
11 if (isIE()) {
12 //IE
13 with (document.documentElement) try {
14 load(key);
15 setAttribute("value", value);
16 save(key);
17 return getAttribute("value");
18 } catch (ex) {
19 alert(ex.message)
20 }
21 } else if (window.sessionStorage) {
22 //FF 2.0+
23 try {
24 sessionStorage.setItem(key, value)
25 } catch (ex) {
26 alert(ex);
27 }
28 } else {
29 alert("Error occured in user data saving. your browser do not support user data.");
30 }
31 }
32
33 function loadUserData(key) {
34 var ex;
35 if (isIE()) {
36 //IE
37 with (document.documentElement) try {
38 load(key);
39 return getAttribute("value");
40 } catch (ex) {
41 alert(ex.message); return null;
42 }
43 } else if (window.sessionStorage) {
44 //FF 2.0+
45 try {
46 return sessionStorage.getItem(key)
47 } catch (ex) {
48 alert(ex)
49 }
50 } else {
51 alert("Error occured in user data loading. your browser do not support user data.")
52 }
53 }
54 function deleteUserData(key) {
55 var ex;
56 if (isIE()) {
57 //IE
58 with (document.documentElement) try {
59 load(key);
60 expires = new Date(315532799000).toUTCString();
61 save(key);
62 }
63 catch (ex) {
64 alert(ex.message);
65 }
66 } else if (window.sessionStorage) {
67 //FF 2.0+
68 try {
69 sessionStorage.removeItem(key)
70 } catch (ex) {
71 alert(ex)
72 }
73 } else {
74 alert("Error occured in user data deleting. your browser do not support user data.")
75 }
76 }

userData和sessionStorage共同的特点就是:这两个对象都可以存储比cookie大的多的多内容。并且不会随每次请求带回到服务器端。但是根据Html5标准和测试发现userData和sessionStorage有很多地方是不同的。

下面是一个测试页面:

31_110118_jg9ncookieVsStoragegif

其中的 SetInsurance link 会操作JavaScript 在IE下用userData写数据, 在FF下用sessionStore写数据。在IE下的情况是:关闭IE或者重启机器写入的值都不会丢失。在FF下的情况很有意思:在本页面写入的值在本 页面可以访问,在由本页面所打开的其它页面可以访问。但是就算本页面开着,在导航栏里输入地址,打开本页面,存入的值就不能访问了。在本页面存入的值,在 它的父页面(打开这个页面的页面)是访问不到的。又看了看Html5标准。sessionStorage 的全名是:Client-side session and persistent storage of name/value pairs 意思估计是存储在Client的内容是有session 会话的,存储的值由session会话所维系,一旦session会话中断或者丢失,存入的值也就随之消失了。所以当页面没有session(父页面,由 地址栏打开的页面),是取不到值的。当FF关闭或者重启机器必然也就取不到值了。
webSQLDatabase
webSQLDatabase在HTML5 标准中是非常Cool的一个东东, 用Javascript写SQL查询,数据库就在浏览器里,这在以前几乎不敢想象。不过今天Safari, Chrome, Opera 都已经支持了,两个webSqlDatabase 的 Demo 页面: http://html5demos.com/database http://html5demos.com/database-rollback
W3C 对WEBSQLDATABASE 的介绍页面: http://dev.w3.org/html5/webdatabase/
WiKi上一个简明的说明: http://en.wikipedia.org/wiki/Web_SQL_Database

From W3C: “…an API for storing data in databases that can be queried using a variant of SQL”
Web SQL Database is supported by Google Chrome[1], Opera and Safari but will not be implemented by Mozilla(Firefox)[2] who instead propone Indexed Database API access.

不知道 HTML 5 的 SQLDB 会被浏览器支持的怎么样, 不过sessionStorage看上去已经可以基本满足需求了。

[转载]数据库操作类库整理

mikel阅读(1069)

[转载]【原创】数据库操作类库整理 – 悟道人生 – 博客园.

一直以来对设计模式,怎么写出高质量、高效、可移植性强的理解不深。也许经验不足是一个很大的原因吧。今天花了很久的时间整理了一个数据库操作类库(简化版),希望能得到各位前辈的指点。

首先,我定义了一个数据库操作接口IDbOperator,该接口实现IDisposable接口,用于操作结束后释放各种资源。类图如下:

属性:

ConnectString用于获取数据库连接字符串

DbConn用于获取当前数据库连接(IDbConnection接口)

方法:

Open():打开数据库连接

Close():关闭数据库连接

GetCommand():获取当前数据源,返回IDbCommand

GetDataSet():获取数据集,返回DataSet数据集

GetSingle<T>():泛型方法,获取单个数据信息

Query():执行SQL语句

QueryTran<T>():执行事务操作

RunProcedure():执行存储过程

由于我想有不少操作,与数据库类型无关。操作是通用的,因此我新建了一个数据库操作抽象类AbstractDbOperator,它实现了接口 IDbOperator,实现了一下方法Open()、Close()、QueryTran<T>()、Query()、 GetSingle<T>()。

并将这些方法定义为虚方法,这样在其继承子类中可以进行重写。

而由于GetCommand()、GetDataSet()、RunProcedure()等方法与数据库类型有关,不同的数据库实现方式不同,因此在不同的数据库操作类中实现。类图如下:

具体的数据库操作类被定义为sealed,同时被定义为internal,因此它们都不允许在程序集以外访问。可能这时有人会问,不让在程序集以外访问,这样的类库有何意义呢?别急,下面还有一个类起决定作用。

新建了一个DBOperator类,用于对外公布数据库操作,它只有一个方法,用于获得数据库操作接口实例。类图如下:

方法:

GetInstance():采用反射机制,分析当前数据库类型,并实例化相应数据库操作类,必须在config文件中appSettings节点下添加如下设置:

1 <add key="assemblyName" value="Higame.Database"/>   <!--程序集名称-->
2 <add key="typeName" value="Higame.Database.OledbOperator"/>  <!--数据库操作类名-->
3 <add key="ConnectionString" value="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=G:\\CSharp\\MonsterEdit\\MonsterEdit\\bin\\Debug\\itemDB.accdb;"/>  <!--连接字符串-->

GetInstance(DatabaseType databaseType):传递数据库类型参数,并在Config文件中appSettings节点下添加连接字符串设置:

1 <add key="ConnectionString" value="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=G:\\CSharp\\MonsterEdit\\MonsterEdit\\bin\\Debug\\itemDB.accdb;"/>     <!--连接字符串-->

GetInstance(DatabaseType databaseType,string connectString):传递数据库类型和连接字符串参数

GetInstance(string connectString):传递连接字符串参数,并在Config文件中appSettings节点下添加如下设置:

1 <add key="assemblyName" value="Higame.Database"/>  <!--程序集名称-->
2 <add key="typeName" value="Higame.Database.OledbOperator"/>  <!--数据库操作类名-->

调用操作如下:

var db = DbOperator.GetInstance(DatabaseType.Access);

感觉整个类库中还有很多地方实现的不是很好。耦合度还是很高,希望各位前辈们不吝赐教。

另附实现源代码:数据库操作类库

作者:zwffff悟道人生 – 博客园
出处:http://zwffff.cnblogs.com/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]ASP.NET用户个性化设置Profile——配置1

mikel阅读(994)

[转载]ASP.NET用户个性化设置Profile——配置1 – 彬 – 博客园.

ASP.NET 2.0除了提供了Membership身份验证机制之外,还提供了一种用户个性化设置的机制,称为Profile。Profile可以和 Membership结合起来,存储用户自定义数据。在Profile中可以存储各种类型的数据,包括最基本的int、double类型,也可以是复杂的 用户自定义类型,比如购物车、用户信息等。

Profile和Session类似,都是用来存储用户自定义数据,但是二者也有本质的区别:

1.Session只能够短暂的保留用户数据(默认为20分钟),但是Profile可以持久的保存,因为二者的存储介质不同,Session 默认是存放在内存当中的,而Profile是存放在数据库中。虽然说Session可以通过配置使之保存在状态服务器或者数据库当中,但还是有很大的区 别。

2.Profile是强类型的,而Session仅仅是一个项集合而已。并且VS对Profile提供了智能感知的支持,使用方便。

下面来看看如何使用Profile,首先第一步和Membership一样,也是添加数据库支持,还是和Membership一样,如果之前按照前面说的添加过了,在这里直接开始第二步,就是配置。若没有,则参考前面的文章添加数据库支持。将下面代码复制入web.config的system.web节点之下:

<profile enabled=”true” defaultProvider=”SQLProvider”>
<providers>
<add name=”SQLProvider” type=”System.Web.Profile.SQLProfileProvider” connectionStringName=”ConnectionString” applicationName=”TestMembership”/>
</providers>
<properties>
<add name=”test” type=”String” allowAnonymous=”true”/>
</properties>
</profile>

这个配置大多数的属性和Membership中的含义是一样的,我们只是说明一下特有的属性。在这个配置当中有Providers和 Properties节点,其中Providers节点之前在配置Membership中了解过了,现在介绍一下properties节点:

在这个节点下,add标签的作用就是添加一个“属性”,name属性就是“属性”的名字,type就是“属性”的类型,这个类型可以是int、 double等简单数据类型,也可以是购物车类等用户自定义数据类型,不过需要注意的是,如果为“类”类型,则该类必须可以序列化,就是在类的上面添加 [Serializable]特性。allowAnonymous这个属性指定用户在匿名状态下,是否可以设置该属性。

好了,配置完毕,我们就可以在页面上使用Profile了,建立一个页面,在页面上添加一个button双击在button的click事件当中写下如下代码:

Profile.test = 123123;

这个Profile.test就是我们在前面properties节点下添加的“属性”,类型为string类型。

浏览这个页面,点击button,看下效果,哈!出错了!错误提示如下:

配置文件属性“test”允许匿名用户存储数据。这要求启用 AnonymousIdentification 功能。

这是因为ASP.NET默认情况下不允许匿名用户,很简单,我们简单的设置一下,允许使用匿名用户即可,还是在web.config中system.web节点下添加一段配置:

<anonymousIdentification enabled=”true”/>

ok,现在再来浏览下页面,点击button试试?没有任何反应?这就对了!

我们再次新建一个页面,在这个页面的page_load事件当中添加一句Response.write(Profile.test);

完事,我们先浏览第一个页面,点击button,执行了Profile.test=“123123”;这段代码,然后关闭浏览器,再打开第二个页面,可以看到,我们在第一个页面中存入的“123123”这个字符串,在第二个页面显示出来了。

原理很简单:当我们打开了anonymousIdentification功能后,ASP.NET会给每个用户分配一个AnonymousID,保存在Cookie中。如图:

这是Cookie中存储的内容,.ASPXANONYMOUS这是默认的Cookie名称,可以在web.config中的anonymousIdentification节中CookieName属性中设置自己的Cookie名字。

这是在调试状态下,得到的AnonymousID

这是数据库aspnet_Users表中存储的内容,注意UserName和IsAnonymous字段。

这是数据库中aspnet_Profile中的内容,可以看到,我们test属性中的内容为:123123,PropertyNames字段中 test:S:0:6的含义大家应该猜出来了吧!test是属性名,S代表的String类型,0:6代表PropertyValuesString字段 中的从第0位到第6位的内容。

这样,当该用户使用这台电脑再次访问网站网站便可以根据这个Cookie中的AnonymousID来识别出用户的身份,再通过AnonymousID从aspnet_Profile表中,便可以取出用户上一次保存的数据。

ok,以上便是匿名状态下的Profile。当然,如果我们不需要匿名,而要求用户登录后才能保存数据的话,那么就没上面这么麻烦了,直接设置allowAnonymous=”false”即可,不需要去添加anonymousIdentification 配置。这样,该属性只能在用户登录状态下访问,未登录去访问该属性就会报错。

本篇文章简单的介绍了下Profile在实际项目中的应用,大家可以照着试一下,先有一个感性认识,下篇继续,会有一个小的项目,来完整的演示一下Profile的应用。

[转载]Attribute(特性),怎么用才更好?

mikel阅读(1206)

[转载]Attribute(特性),怎么用才更好? – 金色海洋工作室 – 博客园.

前几年:
2008年的某一天,我坐火车去北京。硬卧上铺,一晚上就到北京了。爬到上铺之后发现,旁边上铺有一老兄抱着一个笔记本,一开始还以为是看电影呢,仔细一看才发现——老天呀,居然在写代码!

这老兄也太工作狂了,当时可是晚上九点多了呀。屏幕里的IDE和vs有一点像,但又不是。问过了之后才知道,原来是大名鼎鼎的java(具体叫 啥记不清楚了,好像是j2ee,对java相当的不熟,就是那个意思了)。遇到java高手了,不能错失良机,要问问心中的疑问。

于是我就问他“听说java都在用Hibernate,需要把一些信息记录在XML里?”。
老兄说“是呀,以前都这么用。”

“以前?怎么是以前?”

“因为用XML记录信息不方便。”

“那么现在呢?”
“现在用特性了,把需要的信息放在特性里面。”

……

后面又闲聊了一些。

2008年的事情,对话是记不准确了,大体的意思就是这样,一开始用XML,后来用特性。当时我就觉得,俺们.net程序员怎么总是拾人家的牙恵?

现在:

这几天看到了一些关于特性和实体类的文章,中心思想就是用特性记录一些想要记录的信息,用特性把实体类和XX联系起来。

我们先来看看这一篇,C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)
http://blog.csdn.net/RonoTian/archive/2008/09/08/2900714.aspx

给出的示例如下:

public class Person
{
private string _Name;
private int _Age;
private string _Sex;

[DataFieldAttribute(

name, nvarchar)]
public string Name
{
get { return this._Name; }
set { this._Name = value; }
}

[DataFieldAttribute(

age, int)]
public int Age
{
get { return this._Age; }
set { this._Age = value; }
}

[DataFieldAttribute(

sex, nvarchar)]
public string Sex
{
get { return this._Sex; }
set { this._Sex = value; }
}
}

这里用特性([DataFieldAttribute(“name”, “nvarchar”)])来保存字段名和字段类型,把实体类的属性和字段信息连系了起来。
再来看看这个,手把手教你写ORM(六)
http://www.cnblogs.com/alexander-lee/archive/2007/01/24/hbh-orm-06.html

class tb
{
private string _aaa;

[Param(

NChar,10)]
public string aaa
{
get { return _aaa; }
set { _aaa = value; }
}
private string _bbb;

[Param(

NChar, 10)]
public string bbb
{
get { return _bbb; }
set { _bbb = value; }
}
}

这里用特性([Param(“NChar”, 10)])来保存字段类型和字段大小,把实体类的属性和字段信息连系了起来。不同的人有不同的习惯,有不同的需求,所以对以特性的定义也就不一样了。

再来看一篇,ASP.NET MVC杂谈之:—步步打造表单验证框架[重排版](1)
http://www.cnblogs.com/leven/archive/2009/03/26/aspnetmvc_validate_01.html

public class Student{
[Range(
0, 100, “分数的范围必须在{0}和{1}之间.”)]
public int Source{ get; set; }
}

这里用特性([Range(0, 100, “分数的范围必须在{0}和{1}之间.”)])把属性和验证方式联系了起来。

再看一篇(最后一个了),利用Attribute实现的 MVC动态表单
http://www.cnblogs.com/dozer/archive/2010/08/05/DynamicForm.html

[MetadataType(typeof(MusicMetaData))]
public partial class Music
{ }

public class MusicMetaData
{
[DynamicForm(
Create, true, Edit, false, Order = 3)]
public bool IsDeleted { get; set; }

[DynamicForm(

Create, true, Edit, false, Order = 1)]
public bool IsExist { get; set; }

[DynamicForm(

Create, true, Edit, false, Order = 2, Type = 2)]
public string Content { get; set; }
}

这个把属性和添加表单里是否需要,表单里的排序,对应的控件连系了起来。

在18楼的回复里,为了能够应对权限的需求,又增加了“admin = true ,user = false ”来应对。

==============================

好了举了四篇文章里的例子,我们来综合分析一下。如果我觉得他们的做法都挺好,要把他们都吸收进来,那么实体类的定义会变成什么样子呢?

public class Person
{
private string _Name;
private int _Age;
private string _Sex;

[DataFieldAttribute(

name, nvarchar)]
[Param(
nvarchar, 10)]
[Range(
??, “必须填写姓名!”)]
[DynamicForm(
Create, true, Edit, true, Order = 1)]
public string Name
{
get { return this._Name; }
set { this._Name = value; }
}

[DataFieldAttribute(

age, int)]
[Param(
age, 4)]
[Range(
0, 130, “年龄的范围必须在{0}和{1}之间.”)]
[DynamicForm(
Create, true, Edit, true, Order = 2)]
public int Age
{
get { return this._Age; }
set { this._Age = value; }
}

[DataFieldAttribute(

sex, nvarchar)]
[Param(
sex, 1)]
[Range(??, “男或者女”)]
[DynamicForm(
Create, true, Edit, true, Order = 3)]
public string Sex
{
get { return this._Sex; }
set { this._Sex = value; }
}
}

不知道大家对于这样的实体类是不是能够接受?我是接受不了,呵呵。这是有原因的,不是说我不喜欢就不愿意接受,也不是说我是老顽固。

我觉得这么做设计违反了三个原则:最少获知、依赖倒置、单一职责。(在13楼也回复了)

原则不是挂在嘴边上的,是需要在实际里应用的!

1、最少获知:

实体类和字段信息、控件、表单、验证作对应,那么应该知道什么呢?

需要知道字段名、字段大小、字段类型,或者是添加表单是否需要,序号是多少;或者用什么规则来验证?

要知道的也太多了吧。需要什么信息,就增加一个Attribute来保存,是挺简单,但是越积越多,怎么维护呀?

这样就造成了一个问题,Attribute会越来越多,多了就不好维护。Attribute的增加或者改动,就意味着,实体类结构的变化,就是说你要修改你的代码了。

就其原因:违反了最少获知原则。知道的太多了,会累坏的。

2、依赖倒置:

就是要依赖抽象,而不是依赖具体实现。
把具体的信息放在Attribute里面保存,存放的就是一个具体的实现,而不是抽象。Attribute的种类是依据字段名、字段类型,或者是添加表单、修改表单这类的具体的东东,也不是针对抽象来做的。
记录的又是“name”,“nvarchar”,“10”这样的具体的信息,那么就更是具体,离抽象又远了一步。

3、单一职责:

字段名称有变化了,要修改实体类的定义(Attribute也算吧),Create里面要不要显示,需要改的实体类;序号变了,还要改;验证方式变了,还要改。

有一点风吹草动就要改实体类的定义,累不累呢?

============================

如何解决?

说了这么多,都是发现问题,提出问题。估计好多人也都能发现这些,这说这些没有什么意义,我是最反感只提出问题而不解决问题的人,所以我这里要提出我的解决问题的方法。

我不敢保证我的方法就是好方法,但是至少有一个方法,至少是我觉得还可以的方法。

我的做法是,定义一个“字段编号”,比如1000010,前四位是表编号,后三位是字段序号。

这个编号是不能修改的,确定下来就不能再变了。

那么他有什么用处呢?

字段编号没有含义,但是却可以代表很多,比如可以代表字段名、字段大小、字段类型(图1),可以表示表单需要哪些字段(图2,建立视图),可以代表验证方式(图3,建立视图),也可以代表权限(图4,建立视图)……

字段编号什么都可以代表,因为他就是一个编号。他是一个抽象的,本身并没有什么意义,但是却可以代表很多。

有了这个字段编号,实体类就好办了,只需要一个Attribute来保存这个字段编号就可以了,以后有任何的扩展需求,也不需要增加或者改动Attribute的数量和定义。

这样,实体类的定义就变成了

public class Person
{
private string _Name;
private int _Age;
private string _Sex;

[ColumnID(

2000020)]
public string Name
{
get { return this._Name; }
set { this._Name = value; }
}

[ColumnID(

2000040)]
public int Age
{
get { return this._Age; }
set { this._Age = value; }
}

[ColumnID(

2000030)]
public string Sex
{
get { return this._Sex; }
set { this._Sex = value; }
}
}

简洁多了吧。什么?你问,这个特性不易读看不出来是什么不好。这个嘛,属性名称是给程序员看的,而特性是给程序看的,只要不写错就可以。

符合三个规则:

最小获知,只需要知道字段编号。
依赖倒置,依赖字段编号,而不是具体的信息。
单一职责,字段名可以变化,字段大小也可以变,验证方式也可以变,只要字段编号不变,那么就不需要改实体类。

===========================================

图1

图2,这是一个视图

图3,这个也是视图,其实和上面的是一个视图,只是显示的字段不同

图4,这是一个表里的记录,角色、功能节点与字段编号的关系。

[转载]调试JavaScript 错误的解决方案

mikel阅读(1223)

[转载]调试JavaScript 错误的解决方案 – xugang – 博客园.

A  使用alert() 和document.write() 方法监视变量值

如果要中断代码的运行,监视变量的值,则使用alert() 方法;

如果需要查看的值很多,则使用document.write() 方法,避免反复单击“确定”按钮;

B  使用window.onerror 事件

当页面出现异常时,onerror 事件会在window 对象上触发。它能在一定程度上告诉开发者相关的错误信息。

示例:

<script type=text/JavaScript>
function myerror(_message,_url,_line)
{
alert(错误信息: + _message
+\n错误的URI: + _url
+\n错误的行数: + _line
);
return true; //屏蔽系统的事件
}
//绑定错误事件
window.onerror = myerror;
//触发错误示例:
window,onload = test;
</script>

注意:在IE 中,触发error 事件后,正常的代码会继续运行,所有的变量和数据都会保存下来,在其onerror 事件处理方法中可以正常访问到;而在Firefox 中,触发error 事件后,一切都结束,所有的变量和数据都将被销毁。

C  使用 try…catch 语句找错误

示例:

<script type=text/JavaScript>
try
{
alert(触发异常);
}
catch (_ex) //可以省略“_ex”参数
{
var err = 错误信息;
for (var i in _ex)
{
err += \n参数名: + i
+ \t参数值: + _ex[i];
}
alert(err); //打印错误
}
finally //finally 可以被省略…
{
alert(finally 总是会运行);
}
</script>

注意:try…catch 并不能很好的处理JavaScript语法错误

示例:

<script type=text/javascript>
try
{
alert(触发语法错误)); //多了半边“)”
}
catch (_ex) //可以省略_ex参数
{
var err = 错误信息;
for (var i in _ex)
{
err += \n参数名: + i
+ \t参数值: + _ex[i];
}
alert(err); //打印错误
}
</script>

该示例并没有进入catch 块中。

D  使用相关调试器

在IE 和Firefox 浏览器中,可以使用相关的调试器或插件对JavaScript 进行调试。

●  在Firefox 浏览器中,可以使用其自带的“错误控制台”。操作步骤如下:

打开Firefox 浏览器 → 在菜单条“工具”中 → 选择“错误控制台”即可。

在没有其他插件的情况下,其自带的“错误控制台”是一个非常不错的选择。

另外,在Firefox 浏览器中,还有一些很不错的调试器,如:Venkman、Firebug 等。

Venkman 调试器安装后,可以在Firefox 浏览器 → 在菜单条“工具”中 → 选择“JavaScript  Debugger ”命令启用;

Firebug 调试器安装后,可以在Firefox 浏览器 → 在菜单条“工具”中 → 选择“Firebug”→ 选择“打开 Firebug”即可;

●  在IE 浏览器中,可以使用 Microsoft  Script  Debugger 调试器

Microsoft  Script  Debugger 是微软随IE 4 一同发布的一个IE插件,可以从微软的官方网站上免费下载。

下载安装以后,必须将IE 浏览器的调试选项打开才能使用。操作步骤如下:

1>   打开IE 浏览器 → 选择菜单栏的“工具”→ “Internet 选项”命令 → “高级”选项卡 → 将“禁用脚本调试(Internet Explorer )”复选框中的勾去掉即可。

2>   当IE 浏览器正在浏览页面时,运行Microsoft  Script  Debugger 调试器工具即可进行调试。

在Microsoft  Script  Debugger 调试器的 Running  Document 面板中选择开启的页面文件(只读),然后按F9 可以设置断点调试。另外,其Command  Window 面板也是一个很有用的功能,它能在代码断点停止时,在其中输入变量名并回车,便可看到此时变量的值;Command  Window 面板甚至可以接受简单的JavaScript 命令。但Microsoft  Script  Debugger 调试器自身还存在一个bug 问题。

[转载]ASP.NET MVC2.0在Tab页中实现异步无刷新分页

mikel阅读(1121)

[转载]ASP.NET MVC2.0在Tab页中实现异步无刷新分页 – 海纳百川 – 博客园.

概述

很多地方都存在以Tab页来呈现数据的方式,比如网易、新浪、搜狐、QQ等知名的门户网站的首页,还有大家熟知的博客园首页,都是用了tab页来显示数 据。大家之所以喜欢用Tab,因为它能大大的增加显示数据的空间,能在固定的空间中显示更多的数据。分页也是为了方便数据的显示,在应用系统中必不可少。 这篇文章使用JQueryASP.NET MVC中使用Tab页,以及在Tab页中实现异步无刷新的分页功能。估计这个大家都会用得到的。

ASP.NET MVC中实现分页,在之前的一篇博文:ASP.NET MVC2右键菜单和简单分页中已经实现了。实现的方式很简单,在table下面加上一段<a/><a/><a/>…的html就行了。

先介绍一个JQuery插件:Tab,可以到http://jqueryui.com/download上下载。看下它自带一个例子的截图:

ddd

看下本文的成果:

效果图一

gggg

实现:

按照它的Demo,在ASP.NET mvc项目中引入js和css,以及JQuery。我在ASP.NET MVC的母板页中引入这些文件:

    <link href="http://www.cnblogs.com/Content/base/ui.all.css" rel="stylesheet" type="text/css" />
    <script src="http://www.cnblogs.com/Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script src="http://www.cnblogs.com/Scripts/ui.core.js" type="text/javascript"></script>
    <script src="http://www.cnblogs.com/Scripts/ui.tabs.js" type="text/javascript"></script>

引入之后,参考它的Demo在MVC项目中View中使用Tab。 可以看到比tab自带的demo多来了一个getContentTab函数。他有两个参数,用于表示你要显示

哪个tab页的第几页数据。这里默认加载的时候,显示tabs-Shoes的第一页数据。

   <script type="text/javascript">
       $(document).ready(function () {
           $("#tabs").tabs();
           getContentTab('Shoes', 1);
       }); 
    </script>


     <div id="tabs">
        <ul>
      
            <li><a href="#tabs-Shoes" onclick="getContentTab('Shoes',1);">Shoes</a></li>
            <li><a href="#tabs-Electronics" onclick="getContentTab('Electronics',1);">Electronics</a></li>
            <li><a href="#tabs-Food" onclick="getContentTab('Food',1);">Food</a></li>
        </ul>
        <div id="tabs-Shoes">
            
        </div>
        <div id="tabs-Electronics">
            
        </div>
        <div id="tabs-Food">
            
        </div>
    </div> 

当然在定义View之前要先写好控制器的代码,很简单,基本上没有代码:

public ActionResult ViewCategory()
{
    return View();
}

好了,下面开始我们重要的几步。显示table以及实现table的分页。这个demo的tab定义了三个tab页,每一页的table结构是一样的,所以我定义一个用户控件来实现table和分页。代码如下:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcAjaxPaging.Models.ProductViewModel>" %>
<%@ Import Namespace="MvcAjaxPaging.HtmlHelpers"%>
     <table class="grid">
        <thead>
            <tr>
                <th>Product name</th>
                <th>Category</th>
            </tr>
        </thead>
        <tbody>
            <% foreach (var product in ViewData.Model.Products) { %>
                <tr>
                    <td><%= product.Name %></td>
                    <td><%= product.Category %></td>
                </tr>
            <% } %>
        </tbody>
    </table>
        <div class="pager">
        <%= Html.Pager(ViewData.Model.PagingInfo.CurrentPage, ViewData.Model.PagingInfo.ItemsPerPage, ViewData.Model.PagingInfo.TotalItems, "", ViewData["CategoryDisplayName"] as string)%>
    </div>

我们再通过一个ajax调用来将这个控件显示在ViewCategory对应的View上,定义一个js函数:

function getContentTab(categoryName, page) {

    var url = '<%= Url.Content("~/MyPaging/ViewByCategory/") %>' + categoryName + "/"  + page;
    var targetDiv = "#tabs-" + categoryName;
    $.get(url, null, function (result) {
        $(targetDiv).html(result);
    });
}

我们看上面代码,我们去请求服务端的ViewByCategory方法,获取table中的数据。看ViewByCategory的代码:

public ActionResult ViewByCategory(string categoryName, int? page)
{
    categoryName = categoryName ?? this.categories[0];
    int currentPageIndex = page.HasValue ? page.Value : 0;
    ProductViewModel productViewModel = new ProductViewModel();
    
    IList<Product> productsByCategory = this.allProducts.Where(p => p.Category.Equals(categoryName)).ToList();
    productViewModel.Products = productsByCategory.Skip((currentPageIndex - 1) * 10).Take(10).ToList();
    productViewModel.PagingInfo.CurrentPage = currentPageIndex;
    productViewModel.PagingInfo.ItemsPerPage = 10;
    productViewModel.PagingInfo.TotalItems = productsByCategory.Count;
    return View("ViewByCategoryTable", productViewModel);
}

为了简单起见数据来来自内存,使用list的take来实现分页。你可以很方便的改成从DB获取数据。在看下如何生成分页的html,其实很简单,我们只要在生成的分页的HTML中使用getContentTab函数就行了。

public static string Pager(this HtmlHelper helper, int currentPage, int currentPageSize, int totalRecords,string urlPrefix,string status)
{
    StringBuilder sb1 = new StringBuilder();

    int seed = currentPage % currentPageSize == 0 ? currentPage : currentPage - (currentPage % currentPageSize);

    if (currentPage > 0)
        sb1.AppendLine(String.Format("<a href='#' onclick=getContentTab(\"{0}\",\"{1}\") >Previous</a>", status, currentPage));

    if (currentPage - currentPageSize >= 0)
        sb1.AppendLine(String.Format("<a href='#' onclick=getContentTab(\"{0}\",\"{1}\") >...</a>", status, (currentPage - currentPageSize) + 1));

    for (int i = seed; i < Math.Round((totalRecords / currentPageSize) + 0.5) && i < seed + currentPageSize; i++)
    {
        sb1.AppendLine(String.Format("<a href='#' onclick=getContentTab(\"{0}\",\"{1}\") >{1}</a>", status, i + 1));
    }

    if (currentPage + currentPageSize <= (Math.Round((totalRecords / currentPageSize) + 0.5) - 1))
        sb1.AppendLine(String.Format("<a href='#' onclick=getContentTab(\"{0}\",\"{1}\") >...</a>", status, (currentPage + currentPageSize) + 1));

    if (currentPage < (Math.Round((totalRecords / currentPageSize) + 0.5) - 1))
        sb1.AppendLine(String.Format("<a href='#'  onclick=getContentTab(\"{0}\",\"{1}\") >Next</a>", status, currentPage + 2));

    return sb1.ToString();
}

效果:

效果图二:

jjjj

图三:

kkkk

总结:ASP.NET MVC中实现了在tab页中的异步无刷新分页。这东西太常用了,放在这里,希望对你有所帮助。

[转载]ASP.NET MVC2.0 基础教程 实现CRUD操作

mikel阅读(985)

[转载]ASP.NET MVC2.0 基础教程 实现CRUD操作 – 将夏♂未泯‰ – 博客园.

这个演示项目实现了CRUD增删查改操作。

开发环境:Visual Studio 2010 SP1 + Linq To SQLSQL Server 2005

一、Demo演示

1、下载本演示Demo源码:http://files.cnblogs.com/rubyloveromantic/MyMikeDemo.rar

2、Visual Studio 2010打开本演示项目,运行即可。

二、从头开始建立这个演示项目

1、附加下载的Demo源码中的数据库:mike108mvp.mdf 【ok】
2、新建一个MVC Preview应用程序,在Models目录下,添加一个Linq To SQL文件:Mike108mvp.dbml,将数据库中的User表拖入LinqToSql设计窗口中。【ok】
3、web.config中添加命名空间:【ok】
4、在相关目录创建视图文件【ok】


5、Controllers/Users/目录下,新建一个UsersController.cs文件,输入增删查改代码。【ok】

usersControl

public class UsersController : Controller { Mike108mvpDataContext db = new Mike108mvpDataContext(); #region CRUD操作 public ActionResult List() { List<User> model = db.User.ToList(); return View(model); } public ActionResult Create() { return View(); } [HttpPost] public ActionResult Add() { User model = new User(); model.UserId = 0; UpdateModel(model, Request.Form.AllKeys); db.User.InsertOnSubmit(model); db.SubmitChanges(); return RedirectToAction("List"); } public ActionResult Details(int userID) { User model = db.User.FirstOrDefault(e => e.UserId == userID); return View(model); } public ActionResult Edit(int userID) { User model = db.User.FirstOrDefault(e => e.UserId == userID); return View(model); } [HttpPost] public ActionResult Edit(int userID, string parm) { if (parm == "更新") { User model = db.User.FirstOrDefault(e => e.UserId == userID); UpdateModel(model, Request.Form.AllKeys); db.SubmitChanges(); } return RedirectToAction("List"); } public ActionResult Delete(int userID) { User model = db.User.FirstOrDefault(e => e.UserId == userID); db.User.DeleteOnSubmit(model); db.SubmitChanges(); return RedirectToAction("List"); } #endregion #region 数据重置 public ActionResult DataReset() { //批量删除 List<User> oldList = db.User.ToList(); db.User.DeleteAllOnSubmit(oldList); //批量添加 List<User> newlist = new List<User> { new User { UserName = "孙中山", Age = 53, Career = "中国总统" }, new User { UserName = "蒋中正", Age = 36, Career = "中国总统" }, new User { UserName = "小布什", Age = 61, Career = "美国总统" }, new User { UserName = "列宁", Age = 49, Career = "邪恶苏联" }, new User { UserName = "斯大林", Age = 49, Career = "邪恶苏联" }, new User { UserName = "比尔盖茨", Age = 62, Career = "微软老大" }, new User { UserName = "Scott Guthrie", Age = 33, Career = "ASP.NET Leader" } }; db.User.InsertAllOnSubmit(newlist); db.SubmitChanges(); return RedirectToAction("List"); } #endregion }

注:该教程是在学习Mike In Jesus课程是对他的代码作的升级(to MVC2.0)。 无侵权之意。

[转载]介绍一款Memcached服务器软件:NorthScale Memcached Server

mikel阅读(934)

[转载]介绍一款Memcached服务器软件:NorthScale Memcached Server – dudu – 博客园.

今天在找Windows上的64位Memcached软件时,发现了NorthScale Memcached Server

这是由NorthScale公司开发的一个Memcached发行版。

NorthScale公司是由Memcached开源项目的负责人在2009年创建的,总部在加利福尼亚州山景城(Google总部也在这里),创建时获得了500万美元的风险投资。

NorthScale Memcached Server的第一个客户是美国当前最知名的社交网络游戏开发商Zynga Game Network,详见NorthScale Memcached Server Speeds Farmville

Windows平台终于有了正宗的Memcached服务器软件。唯一欠缺的就是没有好 的.NET客户端,NorthScale推荐的客户端是Enyim Memcached Client,但官方网站memcached.enyim.com却无法访问。博客园现在用的就是Enyim Memcached Client,当时在codeplex.com下载的,现在codeplex.com上已经不存在这个项目。

下载安装了NorthScale Memcached Server,安装之后通过http://localhost:8080/就可以访问管理控制台:

可以很方便地监控Memcached的运行情况:

由于是刚发现这个软件,还不熟悉,介绍就到这里,感兴趣的朋友可以研究一下,也期待你分享自己的心得。

相关资源:

官方网站http://www.northscale.com/

官方下载:http://www.northscale.com/download.php?a=d

官方博客:http://blog.northscale.com/

介绍文档:NorthScale_MCDS_Datasheet.pdf

[转载]SQL Server 动态SQL

mikel阅读(1106)

[转载]SQL Server 动态SQL – 潇湘隐者 – 博客园.

动态SQL:code that is executed dynamically。它一般是根据用户输入或外部条件动态组合的SQL语句块。动态SQL能灵活的发挥SQL强大的功能、方便的解决一些其它方法难以 解决的问题。相信使用过动态SQL的人都能体会到它带来的便利,然而动态SQL有时候在执行性能(效率)上面不如静态SQL,而且使用不恰当,往往会在安 全方面存在隐患(SQL 注入式攻击)。
动态SQL可以通过EXECUTE 或SP_EXECUTESQL这两种方式来执行。(来自MSDN)

EXECUTE

执 行 Transact-SQL 批中的命令字符串、字符串或执行下列模块之一:系统存储过程、用户定义存储过程、标量值用户定义函数或扩展存储过程。SQL Server 2005 扩展了 EXECUTE 语句,以使其可用于向链接服务器发送传递命令。此外,还可以显式设置执行字符串或命令的上下文

SP_EXECUTESQL

执 行可以多次重复使用或动态生成的 Transact-SQL 语句或批处理。Transact-SQL 语句或批处理可以包含嵌入参数。在批处理、名称作用域和数据库上下文方面,SP_EXECUTESQL 与 EXECUTE 的行为相同。SP_EXECUTESQL stmt 参数中的 Transact-SQL 语句或批处理在执行 SP_EXECUTESQL 语句时才编译。随后,将编译 stmt 中的内容,并将其作为执行计划运行。该执行计划独立于名为 SP_EXECUTESQL 的批处理的执行计划。SP_EXECUTESQL 批处理不能引用调用 SP_EXECUTESQL 的批处理中声明的变量。SP_EXECUTESQL 批处理中的本地游标或变量对调用 SP_EXECUTESQL 的批处理是不可见的。对数据库上下文所作的更改只在 SP_EXECUTESQL 语句结束前有效。

如果只更改了语句中的参数值,则 sp_executesql 可用来代替存储过程多次执行 Transact-SQL 语句。因为 Transact-SQL 语句本身保持不变,仅参数值发生变化,所以 SQL Server 查询优化器可能重复使用首次执行时所生成的执行计划。

一般来说,我们推荐、优先使用SP_EXECUTESQL来执 行动态SQL,一方面它更加灵活、可以有输入输出参数、另外一方面,查询优化器更有可能重复使用执行计划,提高执行效率。还有就是使用 SP_EXECUTESQL能提高安全性;当然也不是说要完全摈弃EXECUTE,在特定场合下,EXECUTE比SP_EXECUTESQL更方便些, 比如动态SQL字符串是VARCHAR类型、不是NVARCHAR类型。SP_EXECUTESQL 只能执行是Unicode的字符串或是可以隐式转换为ntext的常量或变量、而EXECUTE则两种类型的字符串都能执行。

下面我们来对比看看EXECUTE 和SP_EXECUTESQL的一些细节地方。


EXECUTE (NSELECT * FROM Groups) 执行成功
EXECUTE (SELECT * FROM Groups) 执行成功

SP_EXECUTESQL N
SELECT * FROM Groups; 执行成功
SP_EXECUTESQL SELECT * FROM Groups 执行出错

Summary:EXECUTE 可以执行非Unicode或Unicode类型的字符串常量、变量。而SP_EXECUTESQL只能执行Unicode或可以隐式转换为ntext的字符串常量、变量。

代码

DECLARE @GroupName VARCHAR(50);

SET @GroupName = SuperAdmin;

EXECUTE (SELECT * FROM Groups WHERE GroupName=”’ + SUBSTRING(@GroupName, 1,5) + ””); ‘SUBSTRING’ 附近有语法错误。

DECLARE @Sql VARCHAR(200);
DECLARE @GroupName VARCHAR(50);

SET @GroupName = SuperAdmin;
SET @Sql = SELECT * FROM Groups WHERE GroupName=”’ + SUBSTRING(@GroupName, 1,5) + ””
PRINT @Sql;
EXECUTE (@Sql);

Summary:EXECUTE 括号里面只能是字符串变量、字符串常量、或它们的连接组合,不能调用其它一些函数、存储过程等。 如果要使用,则使用变量组合,如上所示。

代码

DECLARE @Sql VARCHAR(200);
DECLARE @GroupName VARCHAR(50);

SET @GroupName = SuperAdmin;
SET @Sql = SELECT * FROM Groups WHERE GroupName=@GroupName
PRINT @Sql;
EXECUTE (@Sql); 出错:必须声明标量变量 “@GroupName”。

SET @Sql = SELECT * FROM Groups WHERE GroupName= + QUOTENAME(@GroupName, ””)
EXECUTE (@Sql); 正确:

DECLARE @Sql NVARCHAR(200);
DECLARE @GroupName NVARCHAR(50);

SET @GroupName = SuperAdmin;
SET @Sql = SELECT * FROM Groups WHERE GroupName=@GroupName
PRINT @Sql;
EXEC SP_EXECUTESQL @Sql, N@GroupName NVARCHAR,@GroupName
查询出来没有结果,没有声明参数长度。

DECLARE @Sql NVARCHAR(200);
DECLARE @GroupName NVARCHAR(50);

SET @GroupName = SuperAdmin;
SET @Sql = SELECT * FROM Groups WHERE GroupName=@GroupName
PRINT @Sql;
EXEC SP_EXECUTESQL @Sql, N@GroupName NVARCHAR(50),@GroupName

Summary:动态批处理不能访问定义在批处理里的局部变量 。 SP_EXECUTESQL 可以有输入输出参数,比EXECUTE灵活。

下面我们来看看EXECUTE , SP_EXECUTESQL的执行效率,首先把缓存清除执行计划,然后改变用@GroupName值SuperAdmin、CommonUser、CommonAdmin分别执行三次。然后看看其使用缓存的信息

代码

DBCC FREEPROCCACHE;

DECLARE @Sql VARCHAR(200);
DECLARE @GroupName VARCHAR(50);

SET @GroupName = SuperAdmin; ‘CommonUser’, ‘CommonAdmin’
SET @Sql = SELECT * FROM Groups WHERE GroupName= + QUOTENAME(@GroupName, ””)
EXECUTE (@Sql);

SELECT cacheobjtype, objtype, usecounts, sql
FROM sys.syscacheobjects
WHERE sql NOT LIKE %cache%
AND sql NOT LIKE %sys.%;

如下图所示

依葫芦画瓢,接着我们看看SP_EXECUTESQL的执行效率.

代码

DBCC FREEPROCCACHE;

DECLARE @Sql NVARCHAR(200);
DECLARE @GroupName NVARCHAR(50);

SET @GroupName = SuperAdmin; ‘CommonUser’, ‘CommonAdmin’
SET @Sql = SELECT * FROM Groups WHERE GroupName=@GroupName
EXECUTE SP_EXECUTESQL @Sql, N@GroupName NVARCHAR(50), @GroupName;

SELECT cacheobjtype, objtype, usecounts, sql
FROM sys.syscacheobjects
WHERE sql NOT LIKE %cache%
AND sql NOT LIKE %sys.%;

执行结果如下图所示:

Summary:EXEC 生成了三个独立的 ad hoc 执行计划,而用SP_EXECUTESQL只生成了一次执行计划,重复使用了三次,试想如果一个库里面,有许多这样类似的动态SQL,而且频繁执行,如果采用SP_EXECUTESQL就能提高性能。

[转载]利用Attribute实现的 MVC动态表单

mikel阅读(1297)

[转载]利用Attribute实现的 MVC动态表单 – Dozer .NET 技术博客 – 博客园.

一、何谓动态表单

最近再做一个项目,运营只是初步确定了功能,再加上项目比较复杂,所以我不好确定数据库结构

我随时有可能在某个表中加一个属性…

这时候,如果我有2个页面,分别是 Create 和 Edit,那我就需要对这两个页面进行修改~

如果是更多的页面怎么办?

那为何不根据Model,自动生成表单呢?

网上查到一篇文章,是利用外部XML文件,好吧,我承认在一定程度上能方便一点,但写XML和写Html有什么本质区别吗?

二、大家想要怎么样的动态表单?

最懒的方法,只要我数据库和Model有变动,别的地方我不用动一行代码,页面就会自动生成最新的表单!

好理想的状态~ 其实我就是为了这个目标而做的。

虽然上面的方法最方便,但其实并不是如此,因为大部分情况下,表单中不会包含Model所有的属性(比如ID,不可能有吧?~)

另外,Create和Edit的时候,表单也是不同的。

所以,个人感觉,一个比较周全的方法,就是在Model的属性上添加Attribute,告诉程序,哪些属性要生成,哪些不要,它们分别在什么时候生成

上图中,我利用了 MetadataType(为了配合Entity Framework和MVC数据验证,详细请看我另一篇文章:传送门),

然后在MusicMetaData的属性上,加上了Attribute

这个Attribute代表,我在Create的时候,需要输入这个属性;在Edit的时候就不需要;Order很好理解了,就是顺序

然后怎么在页面中使用呢?

就是这么简单,”Create”代表我现在是在Create

后面是一个lambda表达式,传入的是 这个Model属性的名称,和类别(Textbox or TextArea?)

最后,就可以自动生成了动态表单了

三、上码

DynamicForm

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Web.Mvc; namespace DS.Web.MVC { [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property,AllowMultiple=false)] public class DynamicFormAttribute : Attribute { private int order =0; public int Order { get { return order; } set { order = value; } } private int type = 0; public int Type { get { return type; } set { type = value; } } private Dictionary<string, bool> state = new Dictionary<string, bool>(); public bool this[string key] { get { if (state.ContainsKey(key)) { return state[key]; } else { return false; } } } /// <summary> /// 用法示例:DynamicFormAttribute("Create",true,"Edit",false) /// 上述用法:在创建的时候显示,在修改的时候不显示 /// </summary> public DynamicFormAttribute(params object[] states) { for (int k = 0; k < states.Length; k += 2) { state.Add(states[k].ToString(), (bool)states[k + 1]); } } } public static class HtmlExtensions { /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate) { return DynamicForm(htmlHelper, state, ItemTemplate, null, null); } /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <param name="AlternatingItemTemplate">隔行模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate, Func<string, int, string> AlternatingItemTemplate) { return DynamicForm(htmlHelper, state, ItemTemplate, AlternatingItemTemplate, null); } /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <param name="SeparatorTemplate">分隔模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate, Func<string> SeparatorTemplate) { return DynamicForm(htmlHelper, state, ItemTemplate, null, SeparatorTemplate); } /// <summary> /// 动态生成表单 /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="htmlHelper"></param> /// <param name="state">状态</param> /// <param name="ItemTemplate">模板</param> /// <param name="AlternatingItemTemplate">隔行模板</param> /// <param name="SeparatorTemplate">分隔模板</param> /// <returns></returns> public static MvcHtmlString DynamicForm<TModel>(this HtmlHelper<TModel> htmlHelper, string state, Func<string, int, string> ItemTemplate, Func<string, int, string> AlternatingItemTemplate, Func<string> SeparatorTemplate) { var sb = new StringBuilder(); //分析出拥有DynamicFormAttribute的属性,并排序 var props = new List<object[]>(); var meta = typeof(TModel).GetCustomAttributes(typeof(MetadataTypeAttribute), false); if (meta.Length != 0) { foreach (var p in ((MetadataTypeAttribute)(meta[0])).MetadataClassType.GetProperties()) { var attrs = p.GetCustomAttributes(typeof(DynamicFormAttribute), false); if (attrs.Length != 0) { var attr = attrs.FirstOrDefault(a => ((DynamicFormAttribute)a)[state]); if (attr != null) { int index; for (index = 0; index < props.Count; index++) { if ((int)props[index][2] > ((DynamicFormAttribute)attr).Order) { break; } } props.Insert(index, new object[] { p.Name, ((DynamicFormAttribute)attr).Type,((DynamicFormAttribute)attr).Order }) ; } } } } //输出Html for (int k = 0; k < props.Count; k += AlternatingItemTemplate == null ? 1 : 2) { sb.Append(ItemTemplate(props[k][0].ToString(), (int)props[k][1])); if (k + 1 != props.Count) { if (SeparatorTemplate != null) { sb.Append(SeparatorTemplate()); } if (AlternatingItemTemplate != null) { sb.Append(AlternatingItemTemplate(props[k + 1][0].ToString(), (int)props[k + 1][1])); if (k + 2 != props.Count && SeparatorTemplate != null) { sb.Append(SeparatorTemplate()); } } } } //输出 return MvcHtmlString.Create(sb.ToString()); } } }

解释说明:

1、上面一部分是给Model加的Attribute

2、第二部分是HtmlHelper的扩展,用于生成Html代码

四、用法示例

1)生成表单,然后需要隔行更换样式,单行加上class=”1″,双行加上class=”2″,并且2行之间有特殊代码”<br/>”

代码

<%=Html.DynamicForm("Create", (name, type) => "<div class=\"1\">" + Html.TextBox(name).ToString() + "<div/>", (name, type) => "<div class=\"2\">" + Html.TextBox(name).ToString() + "<div/>", () => "<br/>")%>

2)表单中有一个属性Content,需要用 TextArea

代码

namespace EF { [MetadataType(typeof(MusicMetaData))] public partial class Music { } public class MusicMetaData { [DynamicForm("Create", true, "Edit", false, Order = 3)] public bool IsDeleted { get; set; } [DynamicForm("Create", true, "Edit", false, Order = 1)] public bool IsExist { get; set; } [DynamicForm("Create", true, "Edit", false, Order = 2, Type = 2)] public string Content { get; set; } } }

注意上面,我把Content属性的Type改成了2

<%=Html.DynamicForm("Create", (name, type) => type == 2 ? Html.TextArea(name).ToString() : Html.TextBox(name).ToString()) %>

五、

这个想法应该还有很多不完善的地方,所以就先不上示例程序了

如果有什么问题,欢迎大家指出,也可以留言询问各种用法~

所有代码均在上面那块代码段中,可以直接使用~

别忘记添加一些引用~