1.检测到有潜在危险的 Request.Form 值
原因:
(1)在提交数据的页面或webconfig中没有对validateRequest的属性进行正确的设置
(2)HTML里面写了两个 引起
解决:
方案一: 在.aspx文件头中加入这句:
方案二: 修改web.config文件:
以下是引用片段:
<configuration>
<system.web>
<pages validateRequest="false" />
</system.web>
</configuration>
因为validateRequest默认值为true。只要设为false即可。
2.“在没有任何数据时进行无效的读取尝试”解决办法
原因:
所返回的sqldatareader无数据记录,但没有作记录判断力处理。返回的是空值
加上判断即可: if (reader.read()) { TextName.Text =
reader["FieldName"].ToString(); }
3.数据为空。不能对空值调用此方法或属性。
原因:
若对象是null,那么调用对象的方法例如ToString()肯定出错一般是数据库字段的值为空
在grideview等数据控件常出现
解决:因此建议作NULL处理
4.阅读器关闭时 FieldCount 的尝试无效
原因:
使用了SqlDataReader来绑定数据后,将connection对象作了Close()处理
类似
以下是引用片段:
public SqlDataReader GetSomething()
{
conn.open();
SqlDataReader reader =
sqlcmd.ExcecutReader(CommandBehavior.CloseConnection));
conn.close();// occur error here
return reader;
}
在绑定的时候调用了这个方法来指定数据源。如果使用这个方法则需要在调用函数中关闭Re
ader这样conn就可以自动关闭。
如果是使用的是SqlDataAdapter和DataSet那么请去掉显式关闭conn的调用。或者在finally
中调用之。
5.未能映射路径
原因:可能是在webconfig中的路径配置不正确所致
以下是引用片段:
<add key="FCKeditor:BasePath" value="~/admin/fckeditor/"/>
<add key="FCKeditor:UserFilesPath" value="/UserFiles/" />
6.Unreachable code detected
原因:
一般是在异常处理理或返回值时使用了 throw 或return ,可能是其位置放在前面,造成后面的代码执行到了。
解决:
把相关的异常抛出处理的语句(throw)或return 的语句放到代理执行的最后一行。
7.索引超出范围。必须为非负值并小于集合大小
原因:
(1).没有设置DATAKEYFIELD设为数据库中相对应的唯一字段(一般是主键)
(2).DataGrid1.Columns > e.Item.Cells
解决:
(1).设置datakeyfield
(2).加入判断语句datagrid1.item.count (datagrid可以是其它相类似的服务器控件)
8.数据为空。不能对空值调用此方法或属性
9.数据源不支持服务器端的数据分页
解决方法:
不要使用DataReader,改成使用DataSet:或使用自定义分页形式,不采用vs.net提供的分页功能
以下是引用片段:
OleDbDataAdapterda=newOleDbDataAdapter(sql,connection);
DataSetds2=newDataSet();
da.Fill(ds2,"News");
GridView1.DataSource=ds2;
GridView1.DataBind();
10.对象名'***** '无效
原因:当前使用的数据库中没有*****这张表
解决: 查看是否程序中是否写错了所调用的表的名称或看一下SQL数据库中是否存在你所调用的表
11.在建立与服务器的连接时出错。在连接到 SQL Server 2005 时,在默认的设置下 SQL
Server 不允许进行远程连接可能会导致此失败。 (provider: 命名管道提供程序,
error: 40 - 无法打开到 SQL Server 的连接)
solution: 主机上需要用固定的IP地址或服务器地址
12.SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM
之间。
出现这种问题``多半是因为你更新数据库时``datetime字段值为空``默认插入0001年01月01
日``造成datetime类型溢出
13.出现-------表示“属性”,此处应为“方法”
原因:
1.VB与c#的方法,属性的格式有所不一样导致。
2.可能是在design中绑定数据的语法出现错误 解决: 1.记住属性用[] 方法用()
3.记住绑定数据的正确语法(有以下几种方式:: 等)
14.未能从程序集“DAL, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null”中加载类型“DAL.SqlHelper”。
原因:修改了其他层后未能重新编译成dll
解决:编译一下(Rebuild)
15.为过程或函数 指定的参数太多
solution:调用存储过程与定义的存储过程所用的参数数量或所执行的SQL语句中所传入的参
数个数不一致(这是个SQL的错误)
解决方案:仔细检查在存储过程中所设定的参数变量,与实际输入的参数值是否一一对应
16.无法启动调试,绑定句柄无效
原因:系统的Terminal Services没有开启
17.Unable to debugging on the web server.Debug failed because integrated windows authentication is not enable
解决方法:
打开vs2005->工具(Tools)->选项(Option)->调试(debugging)->编辑并继续(Edit and Continue)->全部打勾
18.Automation 操作中文件名或类名未找到: 'RegExp'
解决方法:regsvr32 vbscript.dll
19.System.NullReferenceException: 未将对象引用设置到对象的实例。
原因:
(1)所设置的变量为空值或没有取到值,一般出现在传递参数的时候出现这个问题,也会在使用datagrid或gridview或datalist等数据控件时出现.
(2)控件名称与codebehind里面的没有对应
(3)未用new初始化对象
(4)在程序中所引用的控件不存在
解决方法:
(1)使用try..catch...finally捕捉错误,或直接用response.write()输出所取的变量值
(2)查看代码中是否存在未初始化的变量
20.错误 1718。文件被数字签名策略拒绝(安装vs2005sp1时)
解决方法:
(1). 单击“开始”,单击“运行”,键入 control admintools,然后单击“确定”。
(2). 双击“本地安全策略”。
(3). 单击“软件限制策略”。(注意:如果未列出软件限制,请右击“软件限制策略”,然后单击“新建策略”。 )
(4). 在“对象类型”下,双击“强制”。
(5). 单击“除本地管理员以外的所有用户”,然后单击“确定”。
(6). 重新启动计算机。
微软说明:
http://support.microsoft.com/kb/925336
21.vs2005不能从源文件模式切换到视图模式
解决方法:dos下运行下 devenv /resetskippkgs (win+r cmd)
22.Validation of viewstate MAC failed
解决办法:页面的顶部page加 EnableViewState="False" EnableViewStateMac = "False" 来解决这个问题
23.Automation server can't create object"(Automation 服务器不能创建对)
解决办法:运行: Regsvr32 scrrun.dll 即可。
24.包加载失败
未能正确加载包"Visual Web Developer Trident Designer Package"(GUID =
{30AE7E28-6C02-496D-8E43-85F7A90AEFF1}。
解决方法:
1、设置系统变量 VsLogActivity=1
2、在%APPDATA%MicrosoftVisualStudio8.0文件夹下创建文件ActivityLog.xml
Error
Microsoft Visual Studio
LoadLibrary failed for package [Visual Web Developer Trident Designer Package]
{30AE7E2B-6C02-496D-8E43-85F7A90AEFF1}
--------------------------------------------------------------------------------
8007007e
c:Program FilesMicrosoft Visual Studio 8Common7Packages ridsn.dll
3、从 WINNTMicroSoft.NETFrameworkv2.0.xxx 复制文件gdiplus.dll 到 Program FilesVisual Studio 8Common7Packages文件夹下
4、在命令行下运行命令 :devenv /resetskippkgs
25.Error spawning 'cmd.exe'.
解决方案:把cmd.exe 拷贝到VS安装目录下的VC/BIN目录里,可以解决问题。
26.尝试读取或写入受保护的内存
解决:硬件有问题,一般内存有问题
27.无法在证书存储区中找到清单签名证书
解决方法:用记事本打开项目的.csproj文件,删除类似
28.该项目中不存在目标“ResolveKeySource”。
原因:这个问题很罕见,查询国外相关资料后,大致说是由于部署的不是一个标准的solution, 需要在VS自带的命令提示中运行下面这个东西恢复
解决方法:
(1).将下列代码存为XML文件,命名为TestBuild.xml
以下是引用片段:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetConnectionString>Data Source=.sql2005%3BUser ID=SqlUser%3BPooling=False%3BPassword=X</TargetConnectionString>
</PropertyGroup>
<ItemGroup>
<DatabaseProject Include="Database1Database1.dbproj"/>
<DatabaseProject Include="Database2Database2.dbproj"/>
</ItemGroup>
<Target Name="BuildAllDatabaseProjects">
<MSBuild
Properties="TargetConnectionString=$(TargetConnectionString)"
Projects="@(DatabaseProject)"
Targets="Build;Deploy">
</MSBuild>
</Target>
(2)在命令提示中运行:msbuild /t:BuildAllDatabaseProjects TestBuild.xml
29.不能将值 NULL 插入列 '**'
原因:这是数据库表设计的错误,所涉及的表的字段中设置了不能为空的属性,但是传入的值为null,所以报错
解决方法:查看是否所传入的值是否为Null,或修改数据库中表的所对应的列的属性
30.未能加载类型"命名空间.类"...
原因:
(1)项目修改后没有进行编译
(2)项目中原有的命名空间可能被修改了
(3)项目中所需要的ascx或其他页面不存在,或不包含在项目中
解决方法:
(1)在修改完成之后,请重新生成或编译整个项目或解决方案
(2)手工修改所改动的命名空间,注意名称的大小写,再重新编译
(3)将相关的文件包含在项目中
31.不能访问只读文件“****.***"
解决方案:
给虚拟目录所对应的文件加上“Everyone/写入”权限即可
32.请求因 HTTP 状态 401 失败:Access Denied
解决方法:在iis信息服务器上把匿名访问和允许iis控制密码给勾上
在Web编程中,我们常需要把一些本地文件上传到Web服务器上,上传后,用户可以通过浏览器方便地浏览这些文件,应用十分广泛。
那么使用C#如何实现文件上传的功能呢?下面笔者简要介绍一下。
首先,在你的Visual C# web project 中增加一个上传用的Web Form,为了要上传文件,需要在ToolBox中选择HTML类的File Field控件,将此控件加入到Web Form中,然而此时该控件还不是服务端控件,我们需要为它加上如下一段代码:
<form method=post encType=multipart/ form-data runat="server">
,这样它就成为服务端控件了,如果需要同时上传数个文件时,我们可以相应增加此控件。
需要注意的是代码中一定要把 的属性设置成为:
<form method=post encType=multipart/ form-data runat="server">
如果没有这个属性,就不能实现上传。
然后在此Web Form中增加一个Web Form类的Button,双击Button添加如下代码:
//上传图片的程序段
DateTime now = DateTime.Now ;
//取现在时间到DataTime类的对象now中
string strBaseLocation = "D:/web/FC/pic/";
//这是文件将上传到的服务器的绝对目录
if (uploadfile1.PostedFile.ContentLength != 0)
//判断选取对话框选取的文件长度是否为0
{
uploadfile1.PostedFile.SaveAs(strBaseLocation+now.
DayOfYear.ToString()+uploadfile1.PostedFile.
ContentLength.ToString()+".jpg");
//执行上传,并自动根据日期和文件大小不同为文件命名,确保不重复
Label1.Text="图片1已经上传,文件名为:"+now.DayOfYear.
ToString()+uploadfile1.PostedFile.ContentLength.ToString()+".jpg";
navigator.Insert(System.Xml.TreePosition.After,
XmlNodeType.Element,"pic1","","") ;
navigator.Insert(System.Xml.TreePosition.FirstChild,
XmlNodeType.Text,"pic1","","") ;
navigator.Value= now.DayOfYear.ToString()+uploadfile1.
PostedFile.ContentLength.ToString()+".jpg" ;
navigator.MoveToParent() ;
}
上面的代码用于笔者开发的一个使用XML文件存储新闻信息的系统中,后面几句代码作用是写上传文件信息到XML文件中。如果要上传其他类型文件,只需要将jpg改为相应类型的后缀名即可,如改为doc即可上传Word文件,浏览器即可直接浏览上传的Word文件。
【注意事项】
1. 上传文件不可以无限大;
2. 要注意IIS的安全性方面的配合;
3. 用Visual Studio 的安装项目做安装程序的时候,请注意安装程序所在的绝对路径问题;
4. 注意文件上传后的重名问题。
简介
仅仅使用一行简单的程序,你就能够使你的Windows窗体的所有菜单和上下文菜单具有office2003的菜单外观。同样地,你也可以只用一行程序,就能为你的菜单加上漂亮的图标。本文实现的是一个具有该功能的组件。如果你想让你的菜单恢复原来的外观,也只须调用End方法即可。
组件的使用
要正确使用组件,必须先将你的组件加入到工具箱中。然后将该组件从工具箱中拖放放到form窗体中。这时会看到你的form的设计页中多出了一个名为OfficeMenus1的图标,说明已经将菜单组件加入到了form中。紧接着调用如下方法:
//开始显示office 2003菜单
OfficeMenus1.Start( FormName ); 注:FormName为要改变菜单风格的窗口名称。同样,你也可以通过调用如下方法终止菜单的office2003风格,使之回到原始外观: // 改变菜单的外观风格到原始状态
OfficeMenus1.End();为菜单顶添加图标也很简单,只须为工程添加一个ImageList(图像列表控件),然后将OfficeMenu组件的ImageList属性更改为你添加的ImageList,使用如下代码实现: // 为菜单添加图像
// OfficeMenus.AddPicture( MenuItem MenuItemToAddPictureTo, int ImageIndex );
OfficeMenus1.ImageList = imageList1;
OfficeMenus1.AddPicture(menuItem2, 1);
可以看出,只须如此几行代码就能轻松让你的菜单实现office2003风格。
组件的实现方法及原理
组件由三个类实现,这三个类分别为OfficeMenus,MainMenuItemDrawing和MenuItemDrawing,都位于命名空间Dev4Arabs中。由于实现代码较长,所以在此只给出了组件实现的思路。
组件实现的第一步是从System.ComponentModel.Component类派生类OfficeMenus。定义如下: public class OfficeMenus : System.ComponentModel.Component然后在类中定义两静态变量:
//图像列表用来存储菜单中用到的图标 static ImageList _imageList;// 存储图片细节的一个名称集合,NameValueCollection的详细说明请查阅MSDN,该类主要用来使每个菜单的句柄与每个图标形成一一对应的关系,以便后面绘制菜单顶的图标时快速地找到某个菜单所对应的图标。 static NameValueCollection picDetails = new NameValueCollection();接下来定义公
ODP.NET 11g是Oracle发布的供.NET程序访问Oracle数据库的ADO.NET组件,比微软自带的Oracle组件性能好,更可以访问UDT(User Defined Type)类型,Procedure,REF等等高级Oracle特性。
.NET 1.1的客户端需要的发布文件如下:
◆Oracle.DataAccess.dll (odt111odp.netin1.x)
◆OraOps11.dll (odt111in)
.NET 2.0需要发布:
◆Oracle.DataAccess.dll (odt111odp.netin2.0)
◆OraOps11w.dll (odt111in)
上面的客户端均需要OCI基本包支持:
◆oci.dll
◆oraociei11.dll (也可以用更小的oraociicus11.dll代替)
◆orannzsbb11.dll
为了在客户端测试方便,还可以加上SQL*Plus包,包括两个文件:
◆sqlplus.exe
◆orasqlplusic11.dll
发布sqlplus包可以使用sqlplus "user_name/password@//192.168.1.31:1521/ORCL"在客户端测试Oracle的状态。
根据上面原则,最小的ODP.NET Oracle客户端发布文件包括5个文件,压缩后大小为8MB:
◆oci.dll
◆oraociicus11.dll
◆orannzsbb11.dll
◆Oracle.DataAccess.dll
◆OraOps11.dll
概述
任何有实际价值的关系数据库应用程序都离不开一大堆的查询表。如果您是开发图形用户界面的专家,那么您知道这些查询表将用于加工下拉列表框中的列表。我将查询表分成两种:只读表和可改写只读表。二者的区别在于什么会导致表的改变。我认为如果需要召开员工会议或者用户会议才可以修改表的内容,那么表就是只读的。一个好的例子就是公司的产品类别表。表的内容将不会改变直到公司研发并向市场投放了新产品,或者公司进行了重组。可改写的只读表是内容相对固定的列表,但可以被最终用户修改,通常使用组合框而不用下拉列表框来展现。可改写只读表的一个例子就是称谓术语表。应用程序设计人员能够考虑到最常用的那些称谓,如Ms., Mr., Mrs.以及Dr.,但总有某个用户的头衔是您从未考虑过的而且希望把它添加进来。举个例子看看这种情况有多么常见,我最后工作的一个中型产品有一个设计良好符合第三范式的关系数据库,它包含了350—400张表,我估计了一下大约有250张表是只读的或可改写只读表。
传统的Web应用程序(三层结构应用程序的经典范例)希望尽可能地缓存这种类型的表。这样不仅可以减少往返数据库的次数,还可以降低数据库服务器的查询负载、提高某些应用场景的响应能力,例如接受了新订单。只读表很容易缓存;可以始终将表放在缓存中,偶尔需要重新加载表时,则由数据库管理员(DBA)通过某种方式重新加载缓存。我希望您的企业中很少召开对数据库基本结构和内容作修改的会议。重新刷新中间层缓存中可改写只读表则有一点点麻烦。如果缓存刷新的频率太低就无法获得您期望的效果;用户也无法立刻看到其它用户对数据的修改。支持人员可以使用另一个应用程序添加新项,然后给打算使用该项的同伴发送一条即时消息,但同伴的选择列表框中却并不包含新添加的项。更糟糕的是,如果第二个用户此时试图重新添加这条“缺失的列表项”,就会收到一条数据库错误告知该项已经存在了。由于类似问题的存在,如果可改写只读表允许多点更新,那么通常不进行缓存。
过去,程序员不得不自己手工开发解决方案,使用消息队列、进行文件输出的触发器、或者out-of-band协议来通知缓存,如果应用程序外部的某些用户更新了可改写只读表。这种“通知”解决方案只是通知缓存有行被添加或修改了,指示必须刷新缓存。如何通知缓存有哪些行改变了或者有哪些行被添加了,这一直都是个难题,是分布式数据库和分布式事务或者合并复制领域的问题。低成本的通知解决方案的做法是:只要程序收到“缓存无效“的消息就刷新整个缓存。
SqlDependency 提供了缓存解决方案
如果您正在使用SQL Server 2005和ADO.NET 2.0,那么一种新的称为查询通知的通知解决方案已内置在SqlClient数据供应商和数据库中。该问题终于有了内置且易于使用的解决方案!ASP.NET 2.0也内置了用于支持查询通知的特性。ASP.NET的Cache对象可以注册通知,甚至还可以将通知与ASP.NET的页面缓存或页面片段缓存结合在一起使用。
实现该功能的架构中包含了SQL Server 2005查询引擎、SQL Server Service Broker、sp_DispatcherProc系统存储过程,ADO.NET的SqlNotification (System.Data.Sql.SqlNotificationRequest)和SqlDependency类(System.Data.SqlClient.SqlDependency),以及ASP.NET的Cache类(System.Web.Caching.Cache)简而言之,它的工作方式如下:
1.每个ADO.NET SqlCommand都包含一个Notification属性,表示对通知的请求。
当执行SqlCommand时,如果存在Notification属性,那么就会在网络请求中附加一个表示通知请求的网络协议包(TDS)。
2.SQL Server使用查询通知架构注册所请求通知的订阅,然后执行命令。
3.SQL Server“监视”任何可能导致最初返回的结果集发生变化的SQL DML语句。当发生变生时,就向Service Broker服务发送消息。
4.消息可以:
a.引发通知并将通知传回注册的客户端。
b.驻留在Service Broker's的服务队列中,高级客户端可以实现自己的处理机制。
图1. 通知服务概览
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sqltoolforexcuteandadapter]
(
@objName nvarchar(100),--存储过程名称
@isexcute int --是否为execute 或者是sqladapter 0是execute,1是sqladapter
)
AS
SET NOCOUNT ON
DECLARE @parameterCount int
DECLARE @errMsg varchar(100)
DECLARE @parameterAt varchar(1)
DECLARE @connName varchar(100)
DECLARE @outputValues varchar(100)
--Change the following variable to the name of your connection instance
SET @connName='conn.Connection'
SET @parameterAt=''
SET @outputValues=''
SELECT
dbo.sysobjects.name AS ObjName,
dbo.sysobjects.xtype AS ObjType,
dbo.syscolumns.name AS ColName,
dbo.syscolumns.colorder AS ColOrder,
dbo.syscolumns.length AS ColLen,
dbo.syscolumns.colstat AS ColKey,
dbo.syscolumns.isoutparam AS ColIsOut,
dbo.systypes.xtype
INTO #t_obj
FROM
dbo.syscolumns INNER JOIN
dbo.sysobjects ON dbo.syscolumns.id = dbo.sysobjects.id INNER JOIN
dbo.systypes ON dbo.syscolumns.xtype = dbo.systypes.xtype
WHERE
(dbo.sysobjects.name = @objName)
AND
(dbo.systypes.status <> 1) --不理解这个不等于1是干嘛的?在sql帮助中也没有啊?
ORDER BY
dbo.sysobjects.name,
dbo.syscolumns.colorder
SET @parameterCount=(SELECT count(*) FROM #t_obj)
IF(@parameterCount<1) SET @errMsg='No Parameters/Fields found for ' + @objName
IF(@errMsg is null)
BEGIN
print 'SqlConnection conn = new SqlConnection("");
SqlCommand com = new SqlCommand("'+@objName+'", conn);'
print 'com.CommandType = CommandType.StoredProcedure;'
PRINT ' SqlParameter[] Parameters = new SqlParameter[' +
cast(@parameterCount as varchar) + '];'
PRINT ''
DECLARE @source_name nvarchar,
@source_type varchar,
@col_name nvarchar(100),
@col_order int,
@col_type varchar(20),
@col_len int,
@col_key int,
@col_xtype int,
@col_redef varchar(20),
@col_isout tinyint
DECLARE cur CURSOR FOR
SELECT * FROM #t_obj
OPEN cur
-- Perform the first fetch.
FETCH NEXT FROM cur INTO
@source_name,@source_type,@col_name,@col_order,@col_len,@col_key,
@col_isout,@col_xtype
if(@source_type=N'U') SET @parameterAt='@'
-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN
SET @col_redef=(SELECT CASE @col_xtype
WHEN 34 THEN 'Image'
WHEN 35 THEN 'Text'
WHEN 36 THEN 'UniqueIdentifier'
WHEN 48 THEN 'TinyInt'
WHEN 52 THEN 'SmallInt'
WHEN 56 THEN 'Int'
WHEN 58 THEN 'SmallDateTime'
WHEN 59 THEN 'Real'
WHEN 60 THEN 'Money'
WHEN 61 THEN 'DateTime'
WHEN 62 THEN 'Float'
WHEN 99 THEN 'NText'
WHEN 104 THEN 'Bit'
WHEN 106 THEN 'Decimal'
WHEN 122 THEN 'SmallMoney'
WHEN 127 THEN 'BigInt'
WHEN 165 THEN 'VarBinary'
WHEN 167 THEN 'VarChar'
WHEN 173 THEN 'Binary'
WHEN 175 THEN 'Char'
WHEN 231 THEN 'NVarChar'
WHEN 239 THEN 'NChar'
ELSE '!MISSING'
END AS C)
--Write out the parameter
PRINT ' Parameters[' + cast(@col_order-1 as varchar)
+ '] = new SqlParameter("' + @parameterAt +
@col_name+ '", SqlDbType.' + @col_redef
+ ');'
--Write out the parameter direction it is output
IF(@col_isout=1)
BEGIN
PRINT ' Parameters['+ cast(@col_order-1
as varchar)
+'].Direction=ParameterDirection.Output;'
SET @outputValues=@outputValues+'
?Parameters['+cast(@col_order-1 as varchar) +'].Value;'
END
ELSE
BEGIN
--Write out the parameter value line
PRINT ' Parameters['+ cast(@col_order-1
as varchar) + '].Value = ?;'
END
--If the type is a string then output the size declaration
IF(@col_xtype=231)OR(@col_xtype=167)OR(@col_xtype=175)OR(
@col_xtype=99)OR(@col_xtype=35)
BEGIN
PRINT ' Parameters[' + cast(
@col_order-1 as varchar) +
'].Size=' + cast(@col_len as varchar) + ';'
END
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM cur INTO
@source_name,@source_type,@col_name,@col_order,
@col_len,@col_key,@col_isout,@col_xtype
END
PRINT ''
print ' com.Parameters.AddRange(Parameters);'
if @isexcute = 0 --使用的execute方法执行sql语句
begin
print 'try
{
conn.Open();
com.ExecuteNonQuery();
}
catch (Exception ee)
{
throw ee;
}
finally
{
conn.Close();
}'
end
else if @isexcute = 1--需要返回数据集的话使用这个
begin
print 'try
{
da.Fill(ds);
}
catch (Exception ee)
{
throw ee;
}
finally
{
//do what you want to do or dispose resoures.
}'
end
CLOSE cur
DEALLOCATE cur
END
if(LEN(@errMsg)>0) PRINT @errMsg
DROP TABLE #t_obj
SET NOCOUNT ON
用.Net两年了,也积累了一些知识和经验,觉得应该做出点自己的东西,而并不只是给别人打工。
所以决定利用最新发布的VS2008(Orcas) Beta2也加入到WEB 2.0的大潮中来,一来是学以所用,二来在实践中掌握最新的技术。
现在流行在开发阶段给项目起个Code Name,我也来凑凑热闹,就叫Pluto,以纪念不久前被剥夺九大行星资格的我们天蝎座的守护星——冥王星
平时有自己的工作,只能利用不多的业余时间开发,所以预计(争取)在VS2008正式发布之际,Pluto也能开发完成。
在这里,我会记录下开发Pluto中的一些事情。
WEB 2.0的网站少不了数据库、数据访问,也是一切操作之本,而VS 2008中最大的亮点之一Linq也恰巧是做这个的,所以我的开发从Linq、从数据库开始。网上关于Linq的教学铺天盖地,我不准备重复,我只写下我遇到的问题。
Linq,更新数据怎么就那么费劲?
Linq的全称是Language Integrated Query ,也就是说Linq是以一个查询语言的方式出现在我们面前的。在查询方面Linq做了不少的优化,我们不用在费尽心思去拼装SQL语句、组装实体等,所有操作在Linq里都是强类型的,我们用C#代码轻松地写出漂亮的SQL语句。
那么做为一个查询语言,Linq在数据更新方面又是怎么表现的呢?通常来说Linq的更新会以以下的方式出现(绝大部分教程中都是这么写的)
1var ctx = new MyDataContext();
2var user = ctx.Users.Where(u => u.UserId == userId).Single();
3user.UserName = "New User Name";
4ctx.SubmitChanges();
这些是C#代码,但是背后做了什么呢?Linq会为我们生成类似一下的SQL语句
1--第一步,查询
2SELECT UserId, UserName, FirstName, LastName, CreatTime From User WHERE UserId = @userId
3
4--第二部,更新
5UPDATE User SET UserName = @newUserName
6WHERE UserId = @oldUserId, userName = @oldUserName, FirstName = @oldFirstName, LastName = @oldLastName
发现了什么?首先Linq会取出所有的字段,在user.UserName = "New User Name"的时候,记录下UserName字段被更新过了,UPDATE时会只更新UserName,但是把之前所有字段的值放在WHERE语句里来做为条件。
Are you kidding?! 这样的效率实在是太差了吧?!
抛开效率问题,接下来我们看另外一种更新,有个某个字段记录页面被访问的次数,平时我们会用
1UPDATE POST SET Views = Views + 1 WHERE PostId = @PostId
但是如果我们写下如下C#代码
1var ctx = MyDataContext();
2var post = ctx.Posts.Where(p => p.PostId = @postId).Single();
3post.Views++
4ctx.SubmitChanges();
SQLDMO(SQL Distributed Management Objects,SQL分布式管理对象)封装了Microsoft SQL Server数据库中的对象。SQLDMO是Microsoft SQL Server中企业管理器所使用的应用程序接口,所以它可以执行很多功能,其中当然也包括对数据库的备份和恢复。
SQLDMO由Microsoft SQL Server自带的SQLDMO.dll提供,由于SQLDMO.dll是一个COM对象,所以大家在用之前必须在.NET项目中添加对它的引用
下面是用C#语言书写的用于Microsoft SQL Server数据库备份和恢复的类:
using System;
namespace DbService
{
/// <summary>
/// DbOper类,主要应用SQLDMO实现对Microsoft SQL Server数据库的备份和恢复
/// </summary>
public sealed class DbOper
{
/// <summary>
/// DbOper类的构造函数
/// </summary>
private DbOper()
{
}
/// <summary>
/// 数据库备份
/// </summary>
public static void DbBackup()
{
SQLDMO.Backup oBackup = new SQLDMO.BackupClass();
SQLDMO.SQLServer oSQLServer = new SQLDMO.SQLServerClass();
try
{
oSQLServer.LoginSecure = false;
oSQLServer.Connect("localhost", "sa", "1234");
oBackup.Action = SQLDMO.SQLDMO_BACKUP_TYPE.SQLDMOBackup_Database;
oBackup.Database = "Northwind";
oBackup.Files = @"d:Northwind.bak";
oBackup.BackupSetName = "Northwind";
oBackup.BackupSetDescription = "数据库备份";
oBackup.Initialize = true;
oBackup.SQLBackup(oSQLServer);
}
catch
{
throw;
}
finally
{
oSQLServer.DisConnect();
}
}
/// <summary>
/// 数据库恢复
/// </summary>
public static void DbRestore()
{
SQLDMO.Restore oRestore = new SQLDMO.RestoreClass();
SQLDMO.SQLServer oSQLServer = new SQLDMO.SQLServerClass();
try
{
oSQLServer.LoginSecure = false;
oSQLServer.Connect("localhost", "sa", "1234");
oRestore.Action = SQLDMO.SQLDMO_RESTORE_TYPE.SQLDMORestore_Database;
oRestore.Database = "Northwind";
oRestore.Files = @"d:Northwind.bak";
oRestore.FileNumber = 1;
oRestore.ReplaceDatabase = true;
oRestore.SQLRestore(oSQLServer);
}
catch
{
throw;
}
finally
{
oSQLServer.DisConnect();
}
}
}
}
这段代码虽然很短,但是却很实用,希望能够对大家有所帮助:)
简单地说,LINQ 是支持以类型安全方式查询数据的一系列语言扩展;它将在代号为“Orcas”的下一个版本 Visual Studio 中发布。待查询数据的形式可以是 XML(LINQ 到 XML)、数据库(启用 LINQ 的 ADO.NET,其中包括 LINQ 到 SQL、LINQ 到 Dataset 和 LINQ 到 Entities)和对象 (LINQ 到 Objects) 等。LINQ 体系结构如图 1 所示。
图 1 LINQ 体系结构
让我们看一些代码。在即将发布的“Orcas”版 C# 中,LINQ 查询可能如下所示:
var overdrawnQuery = from account in db.Accounts
where account.Balance < 0
select new { account.Name, account.Address };
当使用 foreach 遍历此查询的结果时,返回的每个元素都将包含一个余额小于 0 的帐户的名称和地址。
从以上示例中立即可以看出该语法类似于 SQL。几年前,Anders Hejlsberg(C# 的首席设计师)和 Peter Golde 曾考虑扩展 C# 以更好地集成数据查询。Peter 时任 C# 编译器开发主管,当时正在研究扩展 C# 编译器的可能性,特别是支持可验证 SQL 之类特定于域的语言语法的加载项。另一方面,Anders 则在设想更深入、更特定级别的集成。他当时正在构思一组“序列运算符”,能在实现 IEnumerable 的任何集合以及实现 IQueryable 的远程类型查询上运行。最终,序列运算符的构思获得了大多数支持,并且 Anders 于 2004 年初向比尔·盖茨的 Thinkweek 递交了一份关于本构思的文件。反馈对此给予了充分肯定。在设计初期,简单查询的语法如下所示:
sequence locals = customers.where(ZipCode == 98112);
在此例中,Sequence 是 IEnumerable 的别名;“where”一词是编译器能理解的一种特殊运算符。Where 运算符的实现是一种接受 predicate 委托(即 bool Pred(T item) 形式的委托)的普通 C# 静态方法。本构思的目的是让编辑器具备与运算符有关的特殊知识。这样将允许编译器正确调用静态方法并创建代码,将委托与表达式联系起来。
假设上述示例是 C# 的理想查询语法。在没有任何语言扩展的情况下,该查询在 C# 2.0 中又会是什么样子?
IEnumerable locals = EnumerableExtensions.Where(customers,delegate(Customer c)
{
return c.ZipCode == 98112;
});
这个代码惊人地冗长,而且更糟糕的是,需要非常仔细地研究才能找到相关的筛选器 (ZipCode == 98112)。这只是一个简单的例子;试想一下,如果使用数个筛选器、投影等,要读懂代码该有多难。冗长的根源在于匿名方法所要求的语法。在理想的查询中,除了要计算的表达式,表达式不会提出任何要求。随后,编译器将尝试推断上下文;例如,ZipCode 实际上引用了 Customer 上定义的 ZipCode。如何解决这一问题?将特定运算符的知识硬编码到语言中并不能令语言设计团队满意,因此他们开始为匿名方法寻求替代语法。他们要求该语法应极其简练,但又不必比匿名方法当前所需的编译器要求更多的知识。最终,他们发明了 lambda 表达式。
目前,基于数据库服务器的桌面管理程序和Web程序已经有太多的应用了,尤其是网络的大量普及,孤立地数据库管理系统无法胜任分布式管理应用,但是面对基于Access数据库的现有的桌面应用我们也无法完全的摒弃。我们利用.Net 远程处理功能将连接和存取Access的行为封装为一个远程对象,供网络中其它客户端通过调用该远程对象来存取实际的Access数据库。我们以 C# 2005 为开发语言来实现上述功能。
一、 技术要点
我们都知道Windows应用程序在运行时会启动一个进程,其总包括若干线程,不同的进程之间通信是开发分布式应用程序所必需的,传统上,这不仅需要深入了解通信流两端上进程的对象,而且还要深入了解低级别协议的宿主、应用程序编程接口以及配置工具等。总之,它是一项需要大量专业知识和经验的复杂任务。
幸好.Net为我们提供了远程处理功能,它所提供的通信方法可以快速而方便地完成上述建立通信的任务。因此,无论是需要快速开发 Web 应用程序,还是要花费更多时间生成关键的企业范围的应用程序,.NET Framework 都会提供支持。通过 .NET 远程处理,客户端应用程序可以使用同一台计算机或其网络中其他任何可用的计算机上的其他进程中的对象。
要使用 .NET 远程处理创建可以让两个对象跨越应用程序直接通信的应用程序,只需生成以下对象即可:
1、 可远程处理的对象。
2、 侦听对该远程对象的请求的应用程序即服务器程序。
3、 对该远程对象发出请求的客户端应用程序。
.Net下不同应用程序中的对象的通信方式有两种:一种是跨应用程序域边界传输对象副本,一种是使用代理交换消息。MarshalByRefObject 是通过使用代理交换消息来进行通信的对象的基类。当跨应用程序使用远程对象时,对象的基类必须是从 MarshalByRefObject 继承。
二、 程序实现
(1)我们先在VS的IDE中创建名为“TestRemoteAccess”的新的解决方案来容纳前述用来实现远程处理的三个项目,首先向解决方案中添加名为“RemoteObject”的类库,然后将默认创建的类名更改为“CRemoteAccess”,并且继承于“MarshalByRefObject”,代码如下:
using System;
using System.Collections.Generic;
using System.Text;
namespace RemoteObject
{
public class CRemoteAccess : MarshalByRefObject
{}
}
我们需要在该对象内创建用于连接和存取本地Access数据库的所有函数,供服务端客户端程序同时调用。用于连接和存取Access数据库的方法这里不再详述,参看附件源码。
首先所有需要向客户端公开的函数其可见性都必须设为 public。变量m_ConnString需要设置为public static,目的是当客户端调用了SetRemoteAccessConnString后将数据库连接字符串保存下来以备在本次连接期间始终能够访问,代码如下:
……
public static string m_ConnString;
……
public void SetRemoteAccessConnString(string Connstr)
{
m_ConnString = Connstr;
}
……
成功连接了Access数据库后我们需要返回数据集给请求的客户端进行显示和编辑,在远程对象中我们声明了几个相关函数:
private void LoadData(string SqlStr, string TableName)
public void SaveData(DataTable ClientDataTable)
public DataTable GetUserTable(string SqlStr, string TableName)
客户端可以传递SQL查询脚本通过调用 GetUserTable来获取相关数据库表的数据,并返回一个DataTable,然后可以将该DataTable附值给DataGridView以便将数据显示出来。GetUserTable通过调用私有的LoadData 函数来完成对数据的获取。SaveData函数用于将编辑过的数据集保存回本地Access数据库文件,代码如下:
……
m_connection.Open();
m_adapter.Update(ClientDataTable);
……
(2)远程对象创建完成,我们需要创建用于侦听该远程对象请求的服务端应用程序。在“TestRemoteAccess”解决方案中新建一个Windows窗体项目名为:“TestServer”,从工具箱中拖拽下几个组件,界面如下所示: