最近的工作是导数据库,把原有库里的内容导入到新的数据库中。
遇到这么一个问题。新数据库有个表有trigger,trigger又调用一个function.function里面是运行CLR加密。(SHIT)
正常插入数据都没有问题,但是我写SQL导入数据,抛异常
An error occurred in the Microsoft .NET Framework while trying to load assembly id 655..
找了一下,微软官方有个结局方案。
To resolve the trust issue, open SQL Management and execute the following SQL statement against every restored/attached database:
You may have to run the statements below against your application database, forms, templates and object databases.
ALTER DATABASE dbname SET TRUSTWORTHY ON
You may have to run this script below against your application database, forms, templates and object databases.
--Run this script against the database that was restored.
--script start
DECLARE @master_owner sysname
SELECT @master_owner = sl.name
from master..syslogins sl
inner join sys.databases sd on
sd.owner_sid = sl.sid
where sd.name = 'master'
DECLARE @my_owner sysname
SELECT @my_owner = sl.name
from master..syslogins sl
inner join sys.databases sd on
sd.owner_sid = sl.sid
where sd.name = DB_NAME()
IF ISNULL(@my_owner, N'') <> @master_owner
BEGIN
DECLARE @SQL1 nvarchar(4000)
SET @SQL1 = N'ALTER AUTHORIZATION ON DATABASE::' + DB_NAME()
+ ' TO ' + @master_owner
EXECUTE (@SQL1)
END
GO
DECLARE @SQL nvarchar(4000)
SET @SQL = N'ALTER DATABASE ' + DB_NAME() + N' SET TRUSTWORTHY ON'
EXECUTE (@SQL)
GO
--script end
Note:
To set this option, you must be a member of the sysadmin fixed server role, which the sa user is by default.
Make sure to log into SQL Mgt Studio, with SQL authentication and the 'sa' user
看来简单的代码实现已经不在话下,那我们再来研究一下函数。
函数就是把一段相关内容的代码整合在一起,赋予一个函数名,函数也可能会有参数和返回值。这个跟C++中的函数是一致的,并且C#中的函数也可以根据参数的不同进行重载。
那么差别在哪里呢?
一个我们要强调的是,在C#中不存在全局函数。使用Main函数的时候我们知道,所有的函数都是属于某个具体的类,哪怕是Main这样的函数也是如此。
另一个就是参数的修饰。回忆一下C 或 C++中的函数参数,我们花了很长时间研究值传递和引用传递(指针传递)的区别。
//值传递 void fun1(string s); //引用/指针传递 void fun2(string * ps);
传参的时候必然要进行copy,值传递的时候系统把值copy到一个局部变量中,局部变量在函数结束后会被释放,所以函数内部对该局部变量的任何修改都不会对调用函数的地方产生影响。
引用传递也会进行copy,系统会把传递的地址copy到一个局部变量中,也就是说,现在,局部变量和外部变量都是指向同一块内存地址进行操作,对该局部变量的操作直接影响外部变量。
那么C#是如何区分的呢。
首先,在C#中没有指针符号。
string str = new string('a',10);
这句话创建了一个由10个a组成的字符串对象,返回的其实是一个指向这块内存地址的指针,只不过C#不再用*来标明了。
再试一下
Object o = new Object();
StringBuilder sb = new StringBuilder("123");
确实如此。
那没有*符号如果来区别值传递和引用传递呢?C#引入了一些参数的修饰符。
//值传递 void fun1(string s); //引用/指针 传递 void fun2(ref string s); //引用/指针 传递 void fun2(out string s);
值传递还是老样子,引用传递C#分出了两种修饰符,ref就是我们常常用的引用传递,调用者赋初值,函数内部可以对他进行改变。out更强调这个值是输出参数,也就是强制要求函数内部必须对这个值进行改变,否则编译无法通过。
另外,调用这些函数的时候,参数的修饰符也同样要加上。
我们举个例子来试试吧。先试试看值类型,int
//值传递 static void Test1(int i) { i=1; } //引用传递 static void Test2(ref int i) { i=2; } //输出参数 static void Test3(out int i) { i=3; } static void Main(string[] args) { int i = 0; Test1(i); Console.Write("{0},", i); Test2(ref i); Console.Write("{0},", i); Test3(out i); Console.Write("{0}", i); } 函数的输出结果是0,2,3
正如我们前面所说,值传递的时候i=1不能影响外部变量,所以i打印还是0,而其他两种引用传递,对内容的修改会直接影响外部数据,所以打印出来分别是2,3
那再来试试看引用类型StringBuilder吧。
static void Test1(StringBuilder sb) {
sb.Append("pass by value"); } static void Test2(ref StringBuilder sb) {
sb.Append("ref"); sb = new StringBuilder("value in Test");
} static void Test3(out StringBuilder sb) { //sb.Append("out");//out参数必须赋值,所以这句无法编译通过 sb = new StringBuilder("value in Test"); } static void Main(string[] args) { StringBuilder sb = new StringBuilder("value in Main. "); Test1(sb); Console.WriteLine( sb); Test2(ref sb); Console.WriteLine( sb); Test3(out sb); Console.WriteLine( sb); } 运行的结果是
value in Main.pass by value
value in Test
value in Test
我们来分析一下:第一句 StringBuilder sb = new StringBuilder("value in Main. "); 创建了一个sb对象。前面说了,其实sb是一个指向sb对象的指针,只不过C#中不用*明确标明了。这样的话,虽然void Test1(StringBuilder sb)是用的值传递的方式,但是sb本身就已经是一个指针,所以对sb的任何修改操作都会体现到外部对象上。因此
sb.Append("pass by value"); 可以实现。推论下来,第二和第三个函数都是引用传递,而传递的内容又已经是一个指针,相当与函数传递的是指针的指针。这样我们不仅可以对sb指向的内容做修改,甚至可以对sb对象本身重新定义。这也就是第二第三个函数调用结果的原因。
做一个猜数的小游戏。程序随机生成一个0~100的数字,让用户猜,不停地给用户提示,猜得数字太大或太小,一直到用户猜中为止。
第一个要解决的问题是随机生成数字如何写。这一点我还是强烈建议同学要学会自己查看msdn。打开msdn,输入Random会看到相关的信息。找到微软示例。自己试试看吧。
第二个要解决的“不停地提示,直到..为止”。用计算机的语言来说,这就是循环。
C#的循环和C++的一样,就是for循环,while循环和do…while循环。
对于C#的容器遍历,还有一个foreach循环。到时候我们再来看看他是怎么工作的。
好了。我们来试试看吧。
Code
如果用户输入的不是数字。那么
int yourNumber = int.Parse(number);
就会抛一个System.FormatException的异常,导致程序意外终止。
跟C++一样,我们可以捕捉住异常进行处理。
try { yourNumber = int.Parse(number); } catch (FormatException e) { Console.WriteLine("让你猜数字!!"); return; }
System.Exception有几个重要的属性,可以帮助了解异常的更多信息。
System.Exception.Message 获取异常的描述字符串
System.Exception.StackTrack 获取异常的栈调用字符串
System.Exception.TargetSite 获取引发异常的方法的信息。
System.Exception.Data 如果你想要在异常中增加额外信息,就需要Data了。
那我们再试一下
try { yourNumber = int.Parse(number); } catch (FormatException e) { Console.WriteLine("Message: {0}",e.Message); Console.WriteLine("Stack: {0}", e.StackTrace); Console.WriteLine("target: {0}", e.TargetSite.Name);//TargetSite不是一个字符串,是一个对象实例 return; }
C#的异常处理还多了一个finally块。放在catch块后。无论异常是否被触发,finally块中的内容始终都会被执行。
try { yourNumber = int.Parse(number); } catch (FormatException e) { Console.WriteLine("Message: {0}", e.Message); Console.WriteLine("Stack: {0}", e.StackTrace); Console.WriteLine("target: {0}", e.TargetSite.Name);//TargetSite不是一个字符串,是一个对象实例 return; } finally { Console.WriteLine("finally"); }
当然,在实际编程中,我们往往会定义自己的异常类,抛出自定义的异常
throw new MyException("I am an exception");
这样做最主要的还是想把异常进行归类,方便处理。
Console.WriteLine("int i = {0}", i);
Console.WriteLine("bool b = {0}", b);
Console.WriteLine("char c = {0}", c);
Console.WriteLine("float f = {0}", f);
Console.WriteLine("double d = {0}", d);
Console.WriteLine("now is {0}", now);
C#中控制台的显示使用System.Console来实现。写就是Console.Write,读就是Console.Read,非常直观。并且我们看到打印是字符串的拼接用的是C字符串拼接的那一套规范,说实话,我对std::cout<<早就看不顺眼了,还是习惯C时代的sprintf,现在开心了,又回到了最熟悉的那种方式。
打印的时候,还可以对内容进行Format
Console.WriteLine("float = {0:f}", 100);Console.WriteLine("Hex = {0:x}", 100);能这样用熟悉的方式写代码让我太高兴了!![]()
我们来写一短小代码吧——设定一个0~100的数字,让用户猜。猜完了给用户提示,告诉他是猜中了还是没猜中。
Code
我们很自然的用了判断语句if..else。正如你所看到C#的判断语句和C++和C都是一样的,要么就是if..else..要么就是switch…case…。
C#对switch…case..有了一点小小的增强。
一方面我们除了可以switch数值类型外,现在还可以switch字符串类型了。
switch(str) { case "c#": ... break; case "c++": ... break; default: break; }
另一方面case或者default后面强制要求存在break语句,否则编译无法通过。
System.String直接继承于System.Object,所以它绝对是一个引用类型。但是——
它有昵称,叫string,也许是为了配合STL中的string吧。
创建一个string对象也不用new,直接赋值就可以了 string str=”123”;
string的比对跟值类型相似,是按内容比对,而不是按地址比对。如果两个string都是 "A"那他们就是相等的。
不管怎么说string还是很好用的。它有我们熟悉的转义符,它有简单的+操作进行字符串连接,它有Length属性反映字符个数,还可以通过Split()方法进行切割。
除了多出一些好用的函数,还有两个地方是C++程序员不熟悉的。
1)@符号可以用在字符串前面,代表转义符失效。
平时我们写C++字符串会这样:
char str[] = "c:\\MyApp\\bin\\debug";
而现在,我们只需要这样:
string str=@"c:\MyApp\bin\debug";
真是简单多了。
2)string是不能改变的。
当我们写:
string s = "123";
s = s + "456";
的时候,系统并不是会原来s对象后面增加了123这个字符串,string是不能被改变的,还记得么。
string s="123"; s = s+"345"; s = s+"456"; s = s+"567";
...
现在,我们的堆里面会有很多string的内存存在,等着被其他人 “delete”的时候才能够被释放。这样也太浪费了吧。
确实如此,如果需要对字符串进行修改操作的时候,我们并不推荐用string这个对象,而是用System.Text.StringBuilder。StringBuilder的内容是可以跟着改变的,这样就避免了刚才的问题。
现在来更正一下前面的代码吧
StringBuilder sb= new StringBuilder("123");
sb.Append("345");
sb.Append("456");
...
上一节我们知道C#的int, double是一种类型,而类型本身可以包含成员变量成员函数,所以C#中的int除了普通赋值外还可以有.操作。
//int的最大值 int.MaxValue //double的绝对值 double.PositiveInfinity //bool的字符串表示 bool.FalseString //char是不是标点符号 char.IsPunctuation('?')
再介绍几个常用的值类型。
decimal也是个浮点数,它比double大一倍,比float大两倍。
还有两个很好用的值类型一个叫做DateTime代表日期,一个叫做TimeSpan代表时间间隔。
DateTime now = DateTime.Now;
DateTime today = new DateTime(2010,7,4);
TimeSpan ts = new TimeSpan(0,10,0);//十分钟
这两个没有昵称的也是值类型啊
值类型只是关心值本身,所以他们跟其他对象不同,比对的时候只是比对值本身,而不是比对对象的内存地址。这也跟C++的int, double用法保持一致了。
值类型也是没有NULL之说的。int只能赋值0,1,-1之类的值。不过有的时候我们确实希望对值类型也赋值null来加以区分。C#提供了?。我们只要把int改成int?就变成了一个可以赋值null的int类型了。其他值类型也是如此。
char? cc = null; bool? bb = null; float? ff = null; DateTime? dt = null;
我们最后再一起来研究一下类型转换。众所周知,把short隐式转换成一个int完全没有问题,反之则不然。由于int能够存储的内容比short多,所以如果你一定要转换,要提供一个强制转换。
short s = (short)IamInt;
short s = checked((short)i);
如果转换的时候确实引起了数据丢失,checked会抛出一个System.OverflowException来提醒你。
由于我对.操作提示出来的内容比较感兴趣,所以实际上我已经很少用(short)i,这种转换方式。而是采用了System.Convert.ToInt16(…)来做转换。Convert可以To很多种,而且To了很多种以后还包含各种转换参数。只要是.出来的东西我都比较放心。![]()
还有一种转换方式是int.Parse(“1”); double.Parse(“1.11”);..他可以把字符串转换成相应的值类型,也是比较方便的。
其实大部分时候,尤其是页面编程的时候,我们需要做的转换都是字符串转数字,这样第一种强制转换是无法使用的。而且后面两种转换,如果不成功也会自动抛出异常。所以还是推荐大家用后面两种转换。
static void Main(string[] args) { int i = 1; bool b = true; char c = 'a'; float f = 0.1f; double d = 0.2; }
这段看似和C++一模一样的局部变量定义代码,其实内部运行的方式有很大的差异。这些int, double不再是以往简单的内建类型,而是C#数据类型的别名。
我们看一下CTS规定的,也就是C#所遵循的系统类型层次结构。
如图所示C#语言是单根的。也就是说所有的类型都集成于一个类,这个类叫做System.Object。所有直接继承于System.Object的类,都叫做引用类型。所有继承于ValueType,并间接继承于System.Object叫做值类型。我们刚才所用的这些类型都是值类型。在图上蓝色表示。
前面说过,C#很注重命名规范,所以这些值类型其实都有他们的“大名”。bool其实叫System.Boolean,int其实叫System.Int32。你看,他们都属于System的命名空间,而且类型的首字母都是大写。不过实际上,大家都喜欢用他们的“小名”,bool, int来称呼他们,这样会感觉更亲切一些。
那么值类型和引用类型到底有什么区别呢。我们学C++的都知道,系统运行起来后,变量都保存在内存中。普通的局部变量保存在叫做“栈”的内存块中,通过遇到{入栈,}出栈的操作,系统可以自动维护局部变量的生命周期。而我们new出来的变量都保存在“堆”中,创建在“堆”中的内存块只能通过我们自己来释放,所以一再强调有new必然要有对应的delete。
在C#中的定义是这样的:所有的值类型都保存在栈中,所有的引用类型都保存在堆中。也就是说,我们刚才定义的int, double,因为他们都是值类型,保存在栈中,所以他们都会在函数结束的时候被系统自动释放掉。而如果我们申明一个字符串或者数组,因为他们是引用类型,保存在堆中,所以函数结束的时候并不会自动释放。而是等待delete被释放。
我们再来搞一下脑子。有这样一段代码
double d = new double();
1)这段代码可行么?2)函数结束时d变量是否还存在在内存中?
首先,这段代码是可行的!double是System.Double类型的简称,new这样一个类型肯定没问题(*注意,C++中只能写成new double, 后面的括号只有在构在函数需要参数的时候才要加上。)
其次,d变量在函数结束时会被自动销毁。虽然它看起来是用了new,但是由于它是值类型,而值类型是保存在栈中的,所以即使看着奇怪,他仍然会被退栈。
值类型引用类型是CTS的根基,很重要!好了,今天就说到这里。
说了那么多道理,我们写一个程序看看。
先熟悉一下开发环境。这次我们就用VS2010。
新建一个控制台项目。VS帮我们生成了这么一段代码。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace test1 { class Program { static void Main(string[] args) { } } } 这段代码似曾相识。它先用using引用了几个命名空间。我们在C++的时候经常使用的是using namespace std; 这里using还是using,namespace这个关键字被省略了。命名空间也变得更规范更有层次了。
接下来是namespace test1{}。这个跟C++中一致,就是给下面的代码设定一个命名空间叫做test1。我们发现C#对命名空间的要求更为强制了。再往后,定义了一个类叫做Program。class Program{}。类的声明依旧没变。只不过原来我们都把类声明在.h文件中。而在C#里面完全没有了.h文件的踪影,也就是说没有了声明和定义之分,我们只要把自己想要的内容写在.cs文件中,如何关联这个问题完全交给编译器去处理。也正因此,再也看不到顶着一长串#include的代码了。最后,有一个main函数。static void Main(string[] args){}。看来在C#中程序的入口还是Main函数。不过还是略微有些差别。void main(int argc,char *argv[])
static void Main(string[] args)
我们已经知道,不同的.NET语言通过各自的编译器,可以编译出相同的中间文件。在运行时,CLR并不知道某个中间文件是由什么语言写出来的,它只需要遵循中间文件的规范,就可以运行该文件了。
能够通过这种方式实现跨语言性还有一个重要因素是所有语言支持的基本类型都是一致的。他们都是符合CTS规范的。
CTS规范属于CLR的范畴之内,它的全称是Common Type System。它规定了符合CTS规范的语言需要支持类型,接口,结构,每句和委托这5种类型,并且还包含这些类型可以有怎样的可见性(public, private, protected),多态的代码实现等等。其实我们发现CTS的这些规范要求和大多数面向对象的编程语言都是一致的,包括和我们学过的C++语言也有很大的相似程度。可以说它从总体上规定了语言的设计方式。
更进一步,如果我们需要多个文件合作运行的话,我们只要把多个中间文件串联在一起,而不用去担心不同的中间语言是由哪种编程语言实现的。
不同的语言虽然都符合CTS规范,但是还是各自有略微的差异。为了能达到不同语言的中间文件可以完全互通,CLR还规定了一套CLS规范。CLS规范其实就是所有语言的交集,因此,如果我们能把程序的类型定义等都定义在这个交集中,任何其他代码都能够识别。也就实现了不同语言的代码互相合作。
最后我们用一张图来说明整个.NET Framework的结构。


这两个没有昵称的也是值类型啊