随笔 - 29  文章 - 1 评论 - 182 trackbacks - 3
<2007年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

欢迎访问我的非技术博客:
http://Moosdau.blog.163.com

与我联系

搜索

 

常用链接

留言簿(3)

随笔分类(28)

随笔档案(29)

最新评论

  • 1. re: 在GridView中显示图片
  • @*梅花* & 一楼的: 首先可以用SELECT 语句直接筛选,类似下面的语句: SELECT CASE WHEN ‘设置了闹钟的条件’ THEN ‘闹钟图片或路径’ ELSE ‘空’ AS ‘...
  • --木刀
  • 2. re: 在GridView中显示图片
  • 请问,怎么让图片在满足一定条件下实现呢? 我向用gridview列出一些日程信息,如果有设置闹钟,在gridview中显示闹钟图片,如果没有设置关闭闹钟,就不显示图片. 而且我的数据绑定时从数据库绑定...
  • --*梅花*
  • 3. re: 多层母版页嵌套中, 内层母版页的事件默认不触发
  • 非常感谢,这个问题我找了一个上午,终于在你这里找到答案。
  • --SJYeah

阅读排行榜

评论排行榜

最近公司的数据库服务器从sql server 2000 升级到了2005, 随之, 就频发奇怪的事:
执行插入或是删除操作时, 最终确认后弹出错误对话框:
the data in row 1 was not committed.
error source: microsoft.sqlserver.management.datatools
error message: the updated row has changed or been deleted since data was last retrieved

首先可以肯定的是, 肯定不是过期了, 我只有两个字段的一个表, 在5秒内录入完数据, 确认, 还是插入失败, 而且也不是SQL SERVER 本身出了严重的故障, 因为在我自己的机器上的服务器就从没发生过这种问题. 在网上找了找, 只看到这一篇:
http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=2660498&SiteID=17&pageid=0#16748041
其它的地方没看到比较实质性的说法, 这篇文章中, 一个人,还不是微软的 , 提到, 在"服务器属性/ 连接/ " 里, 有一个no count 属性, 默认是勾选的, 把它勾掉, 就可以了, 于是找我们的DBA , 把这个选项勾掉, 一试, 果然好了, (要断开重连) 不知道是什么原因, 看起来好像是微软的一个bug.

用sql server 2005 几个月以来, 虽然我尽了最大的努力去适应它, 到现在为止却仍然是觉得要喜欢它真是一件困难的事:
各种常规操作的响应速度远远慢于sql server 2000;
右侧的listview 面板(object explorer detail) 不能自动激活, ( 如果当前激活了其它的标签页, 在左侧的object explorer 上点击一下, 没反应, 还要手动找到右侧面板, 才可以, 而且没有相应的选项. ) 让我郁闷了很久;
存储过程不能双击打开;
...

试用了sql server 2008 CTP, 基本上是照着2005 的模子做的, 唯一感觉惊喜的是写sql 语句也可以用智能感知了, 这实在是一个好东西, 可惜很不稳定, 一会儿有一会儿没有, 有的语句有, 有的语句没有. (select 就有, delete 就没有)  不过, CTP 版么, 难免的, 可以理解.

posted @ 2008-01-17 17:18 木刀 阅读(74) | 评论 (0)编辑
以下一个存储过程:

CREATE PROCEDURE dbo.sp1
AS

 SELECT 'abc'

在C#中:
var o=db.sp1();
GridView1.DataSource=o;
GridView1.DataBind();
会发现, 没有取到任何数据.

原因在于, SELECT 语句没有指定列名, 而LINQ 的查询必须带有列名, 但是如果这时仅仅把存储过程加个列名, 仍然取不到数据, 因为在dbDataContext 中, sp1 函数的定义已经跟原来的SELECT 绑定了, 必须把这个sp1 函数删除, 重新从数据库拖一个过来, 才来取到数据.

从这一点来说, 感觉还是有点不方便, 如果以后的技术能做到, 把表, 存储过程这些东西都自动更新, 都好了. 呵呵.

posted @ 2008-01-17 08:49 木刀 阅读(143) | 评论 (0)编辑
前几天用到多层母版页, 可是奇怪地发现, 内容页面, 以及最外层的母版页事件可以正常触发, 但是内层的母版页就是没有事件发生, 追了很久也没发现为什么, 后来再三检查, 终于发现在页面的html 源的第一行, <% Master %> 指令的"AutoEventWireUp" 属性被置成了false, 由于默认情况下, VS 都会都这个值置成true,所以很少会想到它, 不知道为什么VS 要默认把内层母版页置成不触发事件.
posted @ 2008-01-17 08:44 木刀 阅读(155) | 评论 (2)编辑
     摘要: 后台的key point 就是换页方法. 由于数据很多, 全部取出是不现实的, 我采用的做法是一次取一页, 虽然用户如果连续翻页的话可能会有问题, 不过鉴于这种做法的简单性, 以及用户实际上很少会连续翻页, 都是用查询功能获取数据, 所以就决定采用这种方案. 但是必须让用户知道有多少行, 多少页,就有以下一个初始函数:1protectedvoidInitPage()2{3PageCache=nul... 阅读全文
posted @ 2007-12-26 17:17 木刀 阅读(54) | 评论 (0)编辑

在一个内容比较多的报表页面, 由于要处理查询,换页,编辑等功能,怎样合理布局也不是那么容易就决定的, 这里展示了一种可能的方案. 先看设计视图:


第一行是标题, 第二行是一个显示错误消息的标签, 这是我很喜欢的一种风格, 其一, 相比写脚本来说更可靠一点, 其二, 更重要的是, 我很不喜欢页面弹出对话框让我点确定, 所以我也不想强奸自己的用户, 按说, 这种标签应该放在页面的下方, 不过考虑到用户可能不容易注意到下面的东西, 而这些错误信息往往非常重要, 所以我最终决定把它放在页面的第二行.

第三行是按钮 & 功能区域, 这也是经过很久的尝试决定下来的, 把所有的按钮集中摆放, 当然, 这个页面有点特殊, 它下面还有指向其它页面的链接, 一般的页面下面就没有别的功能性控件了.
第四行是搜索区域, 用多个文本框, 而不是dropdownlist, 更方便于复杂的查询, 而提示标签和文本框是横向排列还是纵向排列, 也是容易发生分歧的地方, 如图所示, 我认为纵向排列看起来更整齐, 所以我采用了纵向排列.
最下面一行还有一个小标签, 是显示搜索条件的.
运行时视图如下,这是把IE 全屏后的截图:




这里面用到了一点小技巧, 在我的页面上, 有这样一段代码:

 1        <script language="javascript" type="text/javascript">
 2            function window.onload()
 3            {
 4                if (dgc!=null
 5                
 6                    dgc.style.width=document.body.clientWidth-dgc.offsetLeft-20;
 7                    dgc.style.height = document.body.clientHeight-dgc.offsetTop-40
 8                    tdLayout.width=window.screen.width-700;
 9                }
 
10            }

11        
</script>

 其中dgc 的定义, 及dataGrid 的headerStyle 如下:

1<div class="ScrollRegion" id="dgc" style="POSITION: relative; TOP: 0px">
2
3<HeaderStyle Font-Size="11pt" Font-Bold="True" Wrap="False" ForeColor="Black" CssClass="Freez" BackColor="#8CCFF7"></HeaderStyle>

这里面用到两个css 属性, ScrollRegion 和Freez , 这两个属性的定义如下:
 1.ScrollRegion
 2{
 3    overflow: scroll; 
 4}

 5
 6
 7.Freez
 8   { 
 9    
10    table-layout:fixed;
11    position:relative;
12    top:expression(this.offsetParent.scrollTop);   
13    z-index: 10;
14   }
 

window.OnLoad 函数的前两行所实现的效果, 就是把datagrid 限定到如运行效果图所示的适合页面的大小, datagrid自身卷动, 而不会让整个页面卷动, 第三行是调整搜索框的间距, 不过"700" 这个值是别的页面的, 这个页面不需要这一句, 原因: 如果一行只有两个文本框, 设三个td , 前两个td设width 各110, 第三个td 不设置, ie 会如何显示? 它并不会听话地让前两格标准地呈现110px 宽, 而是会向后面的空白地址匀, 导致两个文本框看起来离得很远, 解决办法就是为第三个td 也设一个宽度, "挤" 得前两格不能后移. 由于客户端的分辨率不确定, 所以我用了脚本动态设置, 而没有直接写进页面里.

经过这个小技巧, 页面整体看起来就舒服多了.
posted @ 2007-12-26 16:25 木刀 阅读(63) | 评论 (0)编辑
假设一个常见的场景先吧----实际也是我当前的场景-----把一个excel 文件导入到数据库. 这实在是一个常见的功能,但是,没想到的是, 我着实费了一把劲.

实际上,我以前写的有现成的函数来完成这个工作, 但是, 可惜那函数只能在VS2005 下工作, 在2003下面无效,无效的原因是,vs2003 的DataTable.Rows[i].SetAdded() 方法不存在. 也就是说, 你没有办法去更改一行的RowState, 而从excel 读到的dataTable的 行状态"默认" 是unChanged.

如果不信, 你也可以尝试去google或百度上搜一下试试看, 关于这个功能的贴子真是少得可怜,大约大家都换用vs2005了吧, 我找了半天, 零零碎碎地找到一些信息, 这些信息都指向DataTable.GetChanges(RowState) 函数, 由于都是语焉不详, 所以我高兴地认为, 这个函数能将表的所有行的状态都设置为参数所指定的RowState, 就像我原来的函数用一个for循环为每一行调用SetAdded 的效果一样, 我一边还惭愧, 以前用了那么笨的办法. 然而心底却在奇怪, 微软怎么会起了这么笨的一个函数名.

一番测试后, 终于弄明白原来不是微软笨,而是我弄错了, GetChanges函数果然是用来Get, 而不是set的, 它返回母表中指定状态的行组成的子表, 也就是说, 刚才的查找白费了.

再找了一会儿, 借助于Mitch Milam 先生十分专业的一篇文章, 终于找到了正确的solution, 这篇文章的url:
http://blogs.infinite-x.net/2006/09/21/the-saga-of-net-dataadapterupdate-and-multiple-tables/print/
说它专业, 是因为这篇文章几乎是以正规科技论文的格式来写的, 虽然我自己写论文的时候很讨厌麻烦的格式, 但是阅读的时候, 却不得不承认, 这种严谨的格式读起来实在是愉快地多. ----废话少说, 问题的关键在于行状态, 从excel 读取后成了unchanged, 而且又无法修改, 问题就在于读取. 原来数据适配器有一个AcceptChangesDuringFill 属性, 这个属性默认为true, 所以调用Fill 方法后, 它就自动地AcceptChange了 , 然后行状态就变成unchanged了, 只要把这个属性置为false, fill 后, 就可以保持行状态为Added, 然后在update 函数里就不需要考虑行状态了.

以下为不太熟悉这两种操作的朋友列出源代码, 为了阅读方便,去掉了错误检查部分的代码,但在实际应用中, try语句是不可缺少的:
<0>调用代码:
1string path="upload";
2DataTable dt=ImportFromExcel(path);
3string selectCommand="SELECT * FROM T1";
4UpdateToDataBase(selectCommand,dt);

<1> 从excel 读取数据
 1DataTable ImportFromExcel(string path)
 2{
 3    path=Server.MapPath(path);
 4    upload1.PostFile.SaveAs(path);
 5
 6 string connString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path +";Extended Properties='Excel 8.0;HDR=yes;IMEX=1;'";
 7    string selectCommand = " SELECT * FROM [Sheet1$] ";
 8   DataTable dt=new DataTable();
 9
10    System.Data.OleDb.OleDbConnection conn=new System.Data.OleDb.OleDbConnection(connString);
11    conn.Open();
12    System.Data.OleDb.OleDbCommand cmd = new System.Data.OleDb.OleDbCommand(selectCommand, conn);
13    System.Data.OleDb.OleDbDataAdapter adt = new System.Data.OleDb.OleDbDataAdapter(cmd);
14    adt.AcceptChangesDuringFill=false;
15    adt.Fill(dt);
16    conn.Close();
17    System.IO.File.Delete(path);
18    return dt;
19
20}
这里面的trick 就是第14 行.

<2>提交DataTable 到数据库

 

 1void UpdateToDataBase(string sql,DataTable dt)
 2{
 3    string connString=GetConnectionString();
 4
 5   System.Data.SqlClient.SqlConnection con = new System.Data.SqlClient.SqlConnection(connString);
 6   System.Data.SqlClient.SqlDataAdapter adt = new System.Data.SqlClient.SqlDataAdapter(sql, con);
 7   System.Data.SqlClient.SqlCommandBuilder builder = new System.Data.SqlClient.SqlCommandBuilder(adt);
 8    adt.InsertCommand=builder.GetInsertCommand();
 9    con.Open();
10    adt.Update(dt).ToString();
11    con.Close();
12}

13
posted @ 2007-12-21 13:10 木刀 阅读(283) | 评论 (0)编辑

如果有10个文本框在页面上, 并且想设置一下它们的字体, 在VS2005里面, 只要按住ctrl , 把它们都选设一下就好了, 但是在vs2008 , 或是expression web中, sorry, 你必须一个一个地设置它们------vs2008的新的设计器不支持多选.
简直是难以相信. 但这就是事实. 在以下的地址可以看到一个微软的工程师的原贴:
http://forums.asp.net/t/1170907.aspx

这里摘录他的一句话:
Unfortunately, new designer does not support multiple selection (same as Expression Web and earlier FrontPage). This is a popular request so we are considering adding the feature in VS10 (i.e. it will not be in VS 2008).

看来即使开发环境换成vs2008, 还是很必要放一个2005 等在那里, 万一发生了这种需求, 不至于累死.

posted @ 2007-12-21 09:36 木刀 阅读(451) | 评论 (5)编辑

这个功能在VS 中默认是禁用的, 一直没用过, 最近偶然发现, 还真是好用的.

打开方法: 在选项中, 找到"项目和解决方案" 一节, 勾选"在解决方案管理器中跟踪活动项" 即可.

效果: 当一个代码页被选中的时候, 解决方案中相应的文件就会自动被选中, 即使这个文件处于多层文件夹中, vs也会自动把这些文件夹展开, 选中当前文件. 对于比较大的项目来说, 这个功能实在是不错.

posted @ 2007-12-07 09:37 木刀 阅读(187) | 评论 (3)编辑
做一个简单的实验先:
在vs2003中, 新建一个网页, 拖上一个textbox, 将它的readonly 设成true, 然后再放一个input: button, 给它写代码: document.getElementById("txt1").value="test"; 再放一个服务器端的button, 不用写代码.

好了, 开始运行, 点击客户端按钮, 会发现textbox 的值变成了test, 然后再点服务器端按钮使页面回传, 值仍然在, 也就是说, 服务器端的 txt1.Text 属性已经被成功更新了, 事实上, 这时候如果在txt1.OnTextChanged 事件的处理器中下断点, 可以看到事件被触发了.

但是, 在vs2005中, 做同样的动作, 点击服务器端按钮使页面回传后, textbox 的值就会丢掉, 给txt1.OnTextChanged 的处理器下断点, 发现事件没有被触发. 如果readonly 为false, 则服务器端的值被更新.

这一功能在vs 2005中的解决办法有:
<1> 不要将textbox 设为readonly , 而是在pageLoad 中, 为其增加一个readonly attribute, 这样在客户端它仍是只读的, 但是在服务器的眼里, 它不是只读的.
<2>用input:text 或其它代替textbox.
<3>在服务器端用Request.Form[txt1.UniqueID] , 可以取到更改后的值.

any way , 像我们公司一样, 在原来的项目中大量地应用了这一特性, 升级的时候, 大量的无聊工作就不可避免了.
真郁闷.
posted @ 2007-12-06 11:22 木刀 阅读(234) | 评论 (3)编辑

最近被这个问题快折磨死了.

在VS2005 中新建一个AjaxEnabledWebSite, 添加一个母版页, 再添加一个内容页,
在内容页中添加updatePanel, 或是其它的ajax 控件, 都会报错, 一是没有智能感知,
二是在包含在其中任何控件都会报错. -----运行的时候是正确的, 切换到设计视图也是好的,
但是就只是在"源" 视图, 会报错, 如下图所示:



非母版页的页面则是正常的, 虽然不影响运行 , 但是把源视图的代码弄得很乱, 没有一点格式, 我手动
调整以后, 它自己又会乱掉, 而且在编译的时候, 这些全部都被认为是编译错误, 而不是警告, 虽然这些
错误不影响运行, 但是错误列表里几十几百个错误影响到了其它错误的查找, 而且极度影响心情, 哪个老大
知道怎么处理? 

谢谢~~

btw: 我装过ajaxtoolKit 那个工具包, 不知道是不是受它影响.

posted @ 2007-11-30 10:02 木刀 阅读(383) | 评论 (7)编辑

针对这个问题再写一篇文章讨论, 自己也觉得多余, 不过, 由于上一篇中我表意不清, 同时掺杂了一点我对存储过程的不满, 导致整篇文章的中心意思偏离了主题, 在讨论的过程中, 更是越偏越远, 到最后, 连我自己都几乎忘了我的本意. 因此, 觉得有必要再次阐述我原本的立意.

自杀用长枪好,还是用短枪好? 我的回答是, 不要自杀. 这个回答并不真的是客观地评论哪种枪更适合于自杀, 而是, 我根本不关心这件客观的事情, 我的态度是从另一个角度, 说这件事情根本不该发生. 同样的, 其实对于存储过程和sql 语句, 我并不是想去真的挑起它们之间谁更优秀的争吵, 而是说, 在多数情况下, 根本不该去写那样的存储过程.

是的是的, 我绝对承认, 对于几十几百行的存储过程 , 如果把它们拼成字符串由web端提交, 效率肯定会大大低于存储过程, 而我想说的是, 这几十几百行的存储过程, 应该换成一行sql 语句, 其它的部分, 不要由数据库来处理.

这个结论, 以及所有的讨论, 所基于的环境是:
<1>局域网. 所以写公网程序的兄弟就不要把公网的限制也考虑进来了.
<2>大型系统. 所谓的大型系统, 很难界定, 不过我就姑且人为地规定一个参考线: 平均每天1万以上的用户使用,100个以上的用户页面.  如果是小型系统, 只有几十几百个用户, 服务器轻松应付, 甚至web服务器根本和数据库服务器是同一台机子的情况, 就无需讨论了, 小型系统里可以说根本不用考虑性能问题, 只要不是自杀式代码, 都能满足要求.

本人曾先后就职于三家公司, 其中两家的员工数都超过了两万, 公司里有数百条生产线, 每条产线平均约10秒钟过一片产品, 每片产品都要刷条码, 而每条产线至少有十几站, 每站都要刷一下,  也就是说, 光是产线, 每秒都会产生至少几百个数据请求, 而如此庞大的公司, 同时在查报表, 走流程, 从采购->SQE->IQC->......->出货生管->客服 , 不知道有多少人在同时使用系统, 这还只是生产系统, 还有人事系统, 行政管理系统, 针对管理层的决策支持系统, 等等子系统,  -----其实凡是稍微了解一点大企业运作的兄弟肯定都知道这些, 这里为没接触过的朋友啰嗦几句.

这所有的应用中, 查报表是最慢的, 因为报表的需要是千奇百怪的, 有的报表纵深要汇总一个月,甚至几个月的数据, 横向要连接几十张表, 查询一次常常会需要超过一分钟, 而由于在这期间数据库忙于处理这个查询, 必然会延迟其它的请求, 这种交通堵塞效应在每月特定的时候(报表查询高峰) 会极其严重.  即使在平时, 系统的响应速度也几乎完全取决于数据库服务器的响应时间.

以上这么多, 只为了说明一点: 系统的响应时间99%由数据库服务器响应时间决定.  如果不同意这一结论, 就没什么可谈了, 因为我除了亲自工作过的三家公司外, 还听说,见过好几家大公司的情况, 都是这样的, 这是讨论下去的前提.

有了这个前提, 才有后面的结论, 必须尽一切可能减轻数据库服务器的工作量, 某一个报表查询时间如果能缩短0.5秒 , 那整体效应可能就是数分钟, 因为这一个查询早结束0.5秒, 其它的请求就可以早点被处理, 否则交通会越来越堵.

而大量包含业务逻辑的存储过程, 就是增大数据库服务器压力的元凶, 如果把一段50行的存储过程, 改成一个select 语句, 中间的差别多大, 我想不言自明. 

写到这里也就够了, 再写下去, 就又回去存储过程跟内嵌sql谁更好的怪圈了.

[2007/11/2 更新:]
我从来没有对自己的表达能力如此地自卑过, -----唉, 我看我有必要考虑是否要撞墙了.
wangyh 兄一针见血地指出了我关心的问题, 不是内嵌sql VS 存储过程, 而是SQL VS C#, 这才是关键.
xiaotie 兄的论断, 我并不同意, 不过我就不再做进一步的辩论, 因为我现在发现多说一句都可能使问题偏离中心. 既然他质疑我的"如果把一段50行的存储过程, 改成一个select 语句, 中间的差别多大, 我想不言自明. " 这句话, 我就把它改成, 把一段50行的存储过程, 改成只有一句的存储过程, 这应该没有问题了吧?

为了不使sp 派误会, 就假设程序中完全不出现sql 语句, 即使只有一句, 也写成存储过程, 但是, 这些存储过程都不包含逻辑处理, 而是都由C# 去负责, 也即我的标题, 存储过程是长枪, 内嵌sql 是短枪, 逻辑处理是自杀, 我现在不想讨论究竟谁更适合去做逻辑处理, 而是, sql 语言根本不要去做逻辑处理, 只要有可能用C#做的, 都用C#去做, 而不放在sql 语句中.

之所以会从这个讨论变成存储过程vs 内嵌sql, 原因在于, 当写存储过程的时候, 更可能会"不知不觉", "自然而然" 地把逻辑处理写进去, 而写嵌入式语句时, 更可能会把相应的处理用C# 实现. 

应该没有歧义了吧?

posted @ 2007-11-02 11:12 木刀 阅读(2512) | 评论 (47)编辑
[2007.10.31修改本文]
最近与同事在存储过程的问题上发生严重的分歧了.

他认为程序中几乎不应该出现sql 语句, 所有的访问数据库的动作, 都应封装成存储过程. 而我不喜欢存储过程, 不到万不得已, 我不会使用存储过程.

我的想法:
<1>效率.
<1.1>先考虑简单sql 语句, 所谓简单, 比如只有一个select/update/insert/delete 语句, 对简单sql 语句来说, 与存储过程的差异在哪里呢, 存储过程只是预先编译过了, 也就是说, sql语句的执行时间=编译时间+ 执行时间, 而存储过程只有执行时间, 没有编译时间. 而两者的执行时间应该是一样的, 对于简单sql 语句来说, 编译时间非常小, 可以忽略不计, 虽然没有确切的数据, 不过我认为我的这个推断应该没有什么问题. 也就是说, 从效率上讲, 简单sql 语句与存储过程没有什么区别.  我用过各种各样的简单sql语句测试, 这其中也包括链接十几张表的复杂查询, 执行时间跟存储过程没什么差异.
<1.2>复杂sql语句段. 要声明一下, 我认为很长, 譬如几十句, 甚至上百句的sql 段根本不应该存在. 因为比较长的sql语句段一定会包含许多"逻辑" 处理, 而不是"数据" 处理, 举个简单的例子, 像left 这样的字串操作函数, 我觉得根本不应该出现在sql 语句中. --------在服务器中, 数据库服务器是压力最大的服务器, 也是最关键的服务器, web 服务器相对来说压力就小得多, 同等的工作, 应该优先考虑由web服务器来负责, 而不应该转嫁给数据库服务器. 另外, sql 语句虽然经过编译, 但是在类似left 这样的函数处理能力上, 比由C# 这样的高级语言的处理能力还是差得多(这一点不需要证明吧...) , 所以, 我认为所有的逻辑判断, 业务处理, 都应该由高级语言处理, 而sql 语句只处理数据, 这样也可以保证将数据库服务器的压力降到最小.  这一观念的推论, 就可以得出, 长长的sql 段是要不得的, 虽然存储过程擅长sql段, 但是我的程序会保证总是提交给服务器简单sql 语句.

<2>安全性.
提到存储过程, 就会常常提到它对用户的分隔能力, 可是在我参与过的三四个大型项目来看, 这种能力几乎毫无用处, 至少在我所参与的ERP 项目中, 它是毫无用处. 因为最终用户根本不会, 也无需, 也不可能去了解数据库层的东西, 他们只是要求打开一个页面, 可以展示一个报表, 或操作数据, 或完成某个流程等, 实际上, 即使使用存储过程, 在程序中也总是以某一个高权限用户去访问所有的存储过程.

<3>可扩展性.
从这个角度来说, 存储过程就处于完全没有竞争力的地步了. 一个页面很可能会发展变化, 但是每次升级都改动存储过程显然是一件危险的事, 事实上, 一个存储过程一旦写完投入使用, 就很少有人敢去改它. 而sql 语句则可以任意的升级,变化.

<4>可管理性.
由于任何一个项目在其漫长的生命期中都很难预测它到底会膨胀到多大, 所以存储过程的数目也会急剧地增加, 几百个存储过程是很稀松平常的事, 大型项目的存储过程达到几千个一点也不夸张, 如许多的存储过程, 要管理并记清楚每一个存储过程所完成的工作, 实在不是一件容易的事. 尤其是, 写这个存储过程的人离职后, 新来的人多半会被大量的存储过程难倒.

<5>我个人认为使用存储过程很麻烦, 远不如sql语句来得方便.

<6>借助于将表映射成类, 可以很好的支持表结构改变, 这主要是针对从零开始开发, 因为在第一次设计中, 不可能将数据库的结构设计得一次到位, 如果到了后期修改表结构, 所有的旧引用都是运行时错误, 而类型化的sql 语句则会变成编译时错误, 只要全项目替换一下就可以了.

因此, 我的结论是,
<原则1> 只有在很复杂, 需要大量的sql语句时, 才应使用存储过程.
<原则2> 应把所有逻辑处理转移到web服务器, 除极少数特殊情况外, 不应使用sql段.
根据这两个原则, 就很少会有使用存储过程的机会了.

但是, 由于对存储过程缺乏更深入的理解, 所以我对上想法也并没有太大的把握 , 在网上查了不少东西, 也罕有有用的文章, 多半是隔靴搔痒, 或是给新人看的, 或是语焉不详, 一笔带过, 所以就先贴在这儿, 大家在扔砖头之余 , 希望能详细地谈谈这个问题, 也好有个确定的答案.



看了大家的评论, 既有收益, 还是有点无奈, 与我现在面对的状况基本一样, 大家的观点也分成两派, 而且看起来人数也相当, 不过好几位一面倒认为应该使用存储过程的兄弟, 都是"唯心" 式地认为存储过程就是好, 效率就是高, 或者不加说明的下结论, 呵呵, 我觉得这种观点对别人的帮助就很小了, 可能是懒得打字吧.
针对大家的回复, 我再谈几点我的看法.
首先声明, 我是做ERP 的, 系统运行在内网, 公司的网络设施也很好, 所以网络传输部分的代价几乎可以忽略,
另外, 项目的源代码有80多MB(不含图片), 有四五家分公司, 数万人使用, 数据量之大不用说了,当然, 维护也是我们. 有专门的DBA, 但是他很少会管存储过程, 存储过程主要是由我们的项目经理写的.  
我觉得这种环境应该是比较典型的, 写公网上的系统的人应该不会很多吧?

to:@Happiness is enough(21楼)
你说的比较详细也比较中肯, 不过, 你说"没有任何理由可以说明一个project中的成百上千个类和方法不会带来灾难."  这句话我无法认同.  不要说成百上千个, 在.net framework中, 类和方法不止上万吧, 可是也没有带来灾难. 因为类可以按它的用途等等进行层层归类, 而方法封装进类中, 如果这种结构设计得好的话, 即使数量很多,也很容易管理. 另外, 借助于IDE 的智能感知, 以及XML 注释的威力, 我们也无需背下来这些类和方法的名字, 用的时候看一下就可以了. 而存储过程就没办法进行这样的管理.
@koenemy(37楼)
说"你在告诉你同事,,不是几乎,应该是根本,访问数据库的方式要一致。" ,
这句话的后半句我很赞同, 而且事实上几乎必须这样操作, 虽然说, 有的情况比较适合用存储过程, 有的情况比较适合用嵌入式语句, 但是, 在项目的管理上来说, 除非极少数的特殊情况要求, 否则应该用一致的方式来操作, 这也是我觉得为什么一定要在这两者之间挑一个出来作为"主力" 的原因. 因为在项目中还有很多"模棱两可" 的情况, 必须有一个优先的选择.

posted @ 2007-10-30 13:59 木刀 阅读(4695) | 评论 (81)编辑
一个偶然的机会, 发现了<<北京浮生记>> 这个小游戏, 虽然很简单, 但是还挺好玩, 玩了几天, 感觉实在不过瘾, 40天太短, 而且......还有种种的感觉不过瘾的地方 , 于是自己兴起, 写了一个仿作"郑州求生记",  把挣钱的机会发展到了极致, 最高水平的玩家可能会挣到成千上万 个 亿 , 呵呵 , 还是当绝顶富豪过瘾啊.

另外, 原作中健康,名声的作用太小 , 尤其是名声, 好像是完全没用, 实在有悖常理. 在我的改进品中, 可能玩家永远也不希望自己的名声小于100, 呵呵~~

关于游戏本身, 已经有一份很详细的帮助了, 这里不再多说, 祝大家游戏愉快 , 有什么bug 或建议, 请留言告诉我, 谢谢.

----差点忘说了, 游戏运行需要.net framework 2.0

感谢 浮云 的揭发, 重新初始的代码确实有bug, 现已更正, update 到1.61版,
感谢 gxh973121 提出名声可能会扣减为负数, 已经更正, update 到1.62 版, (此问题没有其它影响)
下载地址:
/Files/Moosdau/zzGame1.62.rar
posted @ 2007-09-29 16:27 木刀 阅读(654) | 评论 (9)编辑

把asp.net 2.0 程序部署好以后, 有时会跳出" 无效的字符 " 的错误, 可以通过运行C:\WINDOWS\Microsoft.Net\v2.0.* 文件夹下的aspnet_regiis.exe -i  注册一下解决,  但是奇怪的是, 同样版本的操作系统, 同样地安装vs2005, vs2008,  有的机器无需注册即可正确运行, 有的机器却必须经过这一步,  莫名其妙.

有谁知道原因不?

posted @ 2007-09-28 10:03 木刀 阅读(45) | 评论 (3)编辑

这个方法引自: http://blog.csdn.net/Mark2Win/archive/2007/08/17/1748801.aspx 


在IE7 下, 用window.close() 关闭一个窗口, IE7 总是会弹出一个提示, 问是否关闭这个窗口, 烦都烦死了, 可以让它不弹出提示的代码如下:

 

    window.opener=null;
    window.open('','_top');
    window.top.close();


 

 

posted @ 2007-09-28 09:56 木刀 阅读(431) | 评论 (3)编辑

这种转换主要用来加密js代码.
这两个函数参考了[脚本之家] (http://www.jb51.net/html/200707/23/10560.htm ) 的代码, 但是他的函数是错的, 我不知道为什么那个作者要那样写, 所以我改写了一下 .

function unicode2Chr(str)
{
 if (str!='')
 {
  var fret, tempStr;
  fret = '';
  for (var i = 1; i <= str.length/4; i ++)
  {
   tempStr = str.slice(4*i-4, 4*i);
   fret = fret.concat('%u').concat(tempStr);
  }
  fret = unescape(fret);
  return(fret);
 }
 else
  return('');
}

 


function chr2Unicode(str)
{
 if (str!='')
 {
  var fret, tempStr;
  fret = '';
  for (var i = 1; i <= str.length; i ++)
  {
   tempStr = str.charCodeAt(i - 1).toString(16);
   if (tempStr.length < 4)
    while(tempStr.length <4)
         tempStr = '0'.concat(tempStr);
   tempStr = tempStr.slice(0, 4);
   fret = fret.concat(tempStr);
  }
  return(fret.toUpperCase());
 }
 else
  return(''); 
}

posted @ 2007-09-28 09:54 木刀 阅读(217) | 评论 (0)编辑
这里讨论的是, 增加一个图片列, 这样每一行记录都会附带一个小图片.  如下图所示:

第一列是一个小图示, 第二列显示数据, 这样比单纯显示数据要漂亮许多.

  在GridView 里显示图片, 大抵有两种办法:
<1>添加 ImageField , 绑定到数据源的某一列.
<2>编辑模板, 添加一个带Image 的列.

以下详述两种方法:
<1>添加一个gridview 到页面, 点选它的任务菜单"编辑列",  在弹出的编辑框中, 选择ImageField 并点击添加, 然后选中刚刚添加的列, 右侧面板会显示出它的属性, 在DataImageUrlField 属性中, 填入数据表中的列名. 
 

代码:

<asp:GridView ID="GridView1" runat="server">
            
<Columns>
                
<asp:ImageField DataImageUrlField="img">
                
</asp:ImageField>
            
</Columns>
        
</asp:GridView>


        然后编辑后台代码: 

 DataTable dt = new DataTable();
        DataColumn dc 
=new DataColumn();
        dt.Columns.Add(dc);
        dc
= new DataColumn("img");
        dt.Columns.Add(dc);


        DataRow dr 
= dt.NewRow();
        dr[
0= "11111111111111111";
        dr[
1= ResolveUrl("~/ok.gif");
        dt.Rows.Add(dr);

        dr 
= dt.NewRow();
        dr[
0= "22222222222222222";
        dr[
1= ResolveUrl("~/ok.gif");
        dt.Rows.Add(dr);

        GridView1.DataSource 
= dt;
        GridView1.DataBind();


       编译运行, 就会看到第一幅图片的效果.

<2>编辑模板. 
先编辑前台代码:

        <asp:GridView ID="GridView1" runat="server">
            
<Columns>
                
<asp:TemplateField>
                    
<ItemTemplate>
                       
<asp:Image ID="img1" ImageUrl='<%#Eval("img") %>' runat="server" AlternateText="image lost" />
                    
</ItemTemplate>
                
</asp:TemplateField>
            
</Columns>
        
</asp:GridView>


 

与第一种方式相仿, 它最终的实现机制也是去检索数据源的列名, 所以在Eval 里面, 以字符串的方式给出包含图片url 的列名, 后台代码不变. 最终效果与第一种方式同.

 

木刀认为: 在目前讨论的情况下, 第一种实现方法似乎更好一些, 不过xxField 控件终究有限, 如果要往里面放其它控件, 就只得借助于模板了, 所以第二种方式是更通用, 更强大的选择.

posted @ 2007-09-28 09:49 木刀 阅读(1136) | 评论 (3)编辑

<1>把allowPaging 属性设为true.
<2>处理 PageIndexChanging 事件. 比如:
GridView1.PageIndex = e.NewPageIndex;
rebindData();
其中, rebindData 函数将数据源重新绑定到gridview 控件.

这就行了.

但是再说几个选项.
<1> gridview 有pagesize 属性, 通过调整它, 可以控制在一页上显示记录的条数.
<2>如果嫌默认的选页的样式难看, 可以调整pagerStyle 属性.
<3>如果数据源是用sql 语句从数据库中查出来的, 那么, 因为网页没有全局变量, 所以换页时, 又要重新去数据库查一次, 这可能会是一个问题. 对这个细节, 应该进行优化, 使得只查询一次数据库.  至于使用何种方式保存数据, 仁者见仁, 智者见智, 我就不再说了.
<4>如果结合AJAX 使用, UpdatePanel 的childrenAsTriggers 属性必须设置为 true.

posted @ 2007-09-28 09:44 木刀 阅读(104) | 评论 (1)编辑

  在母版页, 或其它不依赖于母版页的页面上, 都是好的, 但是在依赖母版页的子页上, 所有与AJAX 相关的东西都不能用智能感知了, 而且会提示错误, 包括夹在ContentTemplate 中间的正常控件.

  ---这给排版造成了巨大的问题, 真烦, 这么大的问题, 微软也不给解决一下....

  或者是我用的VS 的版本问题.   在百度上搜一下, 有人说碰到了这样的问题, 也有人说没问题...
有没有人知道怎么解决?

posted @ 2007-09-28 09:43 木刀 阅读(148) | 评论 (1)编辑
UpdateProgress 的使用方法非常简单, 但是却非常有用. 以下先演示它的用法:

<1>. 向页面添加一个updatePanel, 并在里面放一个label 和一个button .
<2>.添加一个UpdateProgress 控件, 并把它的AssociatedUpdatePanelID 属性设置为以上updatePanel的名称.
<3>.切换到"源" 视图, 编辑UpdateProgress 的ProgressTemplate 节点:

    <asp:UpdateProgress AssociatedUpdatePanelID="upInq" ID="upgCUser" runat="server" >
        
<ProgressTemplate>
            
<img id="imgUpg" alt="" src="resources/wait.gif" runat="server" /><span style="COLOR: #ff0000">正在获取数据 </span>
        
</ProgressTemplate>
    
</asp:UpdateProgress>


<4>设定button的点击事件代码:
system.threading.thread.sleep(4000);
<5>好啦, 运行项目, 点击按钮, 就会发现一个旋转的小图标(当然, 取决于你的图片), 和一串红色的字: 正在获取数据, 4秒后, 自动消失.

需要说明的是: 图片是不一定需要的, 也可以只有文字, 在ProgressTemplate 之间的内容是可以随意编辑的, 可以是任意的元素. 另外, 它的DisplayAfter 属性的默认值500 就是比较合适的值, 不建议修改. 这是以毫秒为单位的等待的时间, 当超过这个时间, 它就显示出来, 当updatePanel 更新完毕, 它就隐藏.  所以, 如果把这个时间设置得过短, 每次都会看到UpdateProgress 闪来闪去, 非常影响用户体验.

如果是局域网应用, 服务器总是会很快返回, 就没有必要使用这个控件.

posted @ 2007-09-28 09:39 木刀 阅读(1147) | 评论 (1)编辑
UpdatePanel是最重要的AJAX控件, 它是一个容器, 是局部刷新的单位.

  首先来看它的刷新模式.
  在上一篇笔记中, 我使用了UpdatePanel "主动"指定trigger 的方法, 另外, 也可以在UpdatePanel端不做任何设定, 而"被动" 地接受刷新.

  所以大体来说, 有三种方式使其更新:
  <一>.由UpdatePanel的子控件引起回传, 自动更新.  这种模式是默认的行为, 举个简单的例子, 在UpdatePanel里放一个button, 给button的点击事件写代码, 这样当点击这个按钮时, UpdatePanel进行回传并更新自己的内容. 但是这时, 如果button的点击事件更改了UpdatePanel以外部分的内容, 则更新不会马上显示出来, 因为整页并没有更新, 这个实验上一篇笔记已经描述了.

<二>UpdatePanel指定一个非子控件的控件, 而这个trigger 自己并不知道它会引起整页回传还是UpdatePanel的局部回传. 这种做法就是上一篇所使用的方法. 设置UpdatePanel的Triggers 属性, 而外部的button 本来应该是引起整页回传的, 但是经过UpdatePanel的声明, 这个button 就变成了UpdatePanel的独占的了, 无需经过这个button的同意.

<三>UpdatePanel自己不知道自己何时将被更新, 完全取决于其它控件. 这种情况下, 必须把它的UpdateMode设为Conditional, 这样, 任何一个函数都可以调用它的Update 方法使其重绘, 无论是否子控件都无关紧要.

有了以上的讨论, 再看UpdatePanel 的几个关键属性就很容易理解了, 如果要使用第二, 第三种方式使其更新 ,必须将UpdateMode属性设置了Conditional, 而第一种方式则必须设置为Always,  如果希望子控件能够触发事件, 则把ChilderAsTriggers 设置为true, 否则设为false.

UpdatePanel可以互相嵌套, 但是这种嵌套只是外观上的嵌套, -----内层的UpdatePanel 更新时, 并不会引起外层的UpdatePanel 重绘.

然后再看UpdatePanel 的排版.

UpdatePanel没有height, width 等属性, 所以也就无法设定其尺寸, 然而从工具箱里拖过来以后, 它会有一个默认的大小, 如果往里面往两个控件, 这两个控件就会一上一下地叠起来, 而不会排成一排 . 如果希望子控件排 成一排, 则需要在UpdatePanel 的代码页里, 写一个div, 把所有内容都放进这个div 里面, 然后设定div的宽度 大于等于所有控件横排的宽度, 则这些控件就会自动排 成一排了.

posted @ 2007-09-28 09:38 木刀 阅读(323) | 评论 (0)编辑
     摘要: 首先准备环境, 从微软下载ASP.NET AJAX 1.0 Extentions(网址:http://asp.net/ajax/downloads/default.aspx) , 当然,首先应该安装VS2005, 在2003 上是否可行, 我不知道, 因为没有试过. 安装完成以后, 打开VS2005, 新建一个网站, 会看到比原来多出来一项"ASP.NET AJAX-Enabled web sit... 阅读全文
posted @ 2007-09-28 09:36 木刀 阅读(281) | 评论 (0)编辑

整个算法的流程是:
接收方先同时生成公钥和私钥, 再把公钥传递给发送方, 发送方收到公钥后, 用此公钥将自己的明文加密, 然后将加密后的密文传递给接收方, 接收方用自己的私钥解密得到明文. 以下是演示这个过程的示例代码:
 

           

 //待加密的明文
            string originText="Hello";
            
//公钥
            string publicKey;

            System.Security.Cryptography.RSACryptoServiceProvider rsaReceive 
= 
                
new System.Security.Cryptography.RSACryptoServiceProvider();
            System.Security.Cryptography.RSACryptoServiceProvider rsaSend 
=
                
new System.Security.Cryptography.RSACryptoServiceProvider();

            
//接收方先生成公钥, 并将此公钥公开
            
//参数false 表示只生成公钥, 如果为true, 则同时生成公钥和私钥.
            publicKey = rsaReceive.ToXmlString(false);
            
//发送方接收公钥, 并用此公钥加密数据
            rsaSend.FromXmlString(publicKey);

            
//发送方执行加密.
            
//第二个参数指示是否使用OAEP, 如果使用, 则程序必须运行在Windows XP 及以上版本的
            
//系统中. 无论true 或false, 解密时必须跟加密时的选择相同. 
            byte[] cryp = rsaSend.Encrypt(System.Text.Encoding.UTF8.GetBytes(originText),false);
            
//接收方用自己的私钥解密
            byte[] b_OriginText = rsaReceive.Decrypt(cryp, false);
posted @ 2007-09-28 09:30 木刀 阅读(793) | 评论 (1)编辑

普通的邮件, 用System.Net.Mail 类 或 System.Web.Mail 类 处理即可, 但是Exchange Server 环境下, 这两个类起不了作用-------至少目前我看到的情况如此.

整个过程如下:

1. 先添加COM 引用 "Microsoft CDO for Windows 2000 Library" .
2. 发送邮件的代码:
 

CDO.Message msg = new CDO.Message();

        
string passWord="passWord";

        
string from = "my@domain.com";

        
string server = "192.168.0.0";


        msg.From 
= from;
        msg.To 
= from;
        msg.Subject 
= "test mail";
        msg.TextBody 
= "test.";

 

        CDO.IConfiguration iConfig 
= msg.Configuration;

        ADODB.Fields fields 
= iConfig.Fields;

 

        fields[
"http://schemas.microsoft.com/cdo/configuration/sendusing"