代码生成器 SQLMetal

随LINQ一起来的工具里,有个Entity Class Generator Tool,叫SQLMetal,能连接到数据库直接产生跟数据表对应的Entity Class,就象上个帖子里的例子一样

SqlMetal /server:(local) /database:DLINQ /delayfetch /pluralize /namespace:LINQ.Examples.QuickStart /code:User.cs

也可以先生成一个Schema XML文件,譬如

SqlMetal /server:(local) /database:DLINQ /pluralize /xml:User.xml

对应上个贴里的Users表的XML是这样的

<Database xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” Name=”DLINQ”>
  <Schema Name=”Dbo”>
    <Table Name=”users” Property=”Users” Class=”User”>
      <Column Name=”LogonID” Type=”System.String” DbType=”NVarChar(20) NOT NULL” Nullable=”false” IsIdentity=”true” />
      <Column Name=”Name” Type=”System.String” DbType=”NVarChar(40)” />
      <Column Name=”Password” Type=”System.String” DbType=”NVarChar(20)” />
      <Column Name=”EmailAddress” Type=”System.String” DbType=”NVarChar(40)” />
      <Column Name=”LastLogon” Type=”System.DateTime” DbType=”DateTime” Nullable=”true” />
      <PrimaryKey Name=”PK__users__09DE7BCC”>
        <Column Name=”LogonID” />
      </PrimaryKey>
      <Index Name=”PK__users__09DE7BCC” Style=”CLUSTERED” IsUnique=”true”>
        <Column Name=”LogonID” />
      </Index>
    </Table>
  </Schema>
</Database>

你可以修改其中内容 (但居然无法修改生成的property name??),然后再运行SQLMetal来产生编码

SqlMetal /namespace:LINQ.Examples.QuickStart /code:User.cs User.xml

注意,生成的Entity Class是个partial class

public partial class User : System.Data.DLinq.IChangeNotifier
{
  //….
}

里面是对应数据表字段的变量和property们,你可以在另外的文件里定义该类的操作,譬如

public partial class User
{
  public void Test()
  {
     Console.WriteLine(“User:Test():” + Name);
  }

        //…..
}

这样,即使你用SQLMetal重新生成Entity Class文件,也不会影响你定义操作的文件。将来IDE大概会支持,或者顶多在Build过程中多加一步

DLINQ练习

以前做过一个NHibernate练习,这里用DLINQ重新来过

 

想试的话,也许需要改动连接字符串,可惜上面的程序在8月份的CTP上不工作。出錯信息是

Unhandled Exception: System.TypeLoadException: Could not load type ‘System.INullableValue’ from assembly ‘mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’.
   at System.Data.DLinq.SqlClient.QueryConverter.IsConstNull(ConstantExpression expr)
   at System.Data.DLinq.SqlClient.QueryConverter.VisitConstant(ConstantExpression cons) in c:\PdcBuild\query\DLinq\Engine\Converter.cs:line 500
   at System.Data.DLinq.SqlClient.QueryConverter.VisitInner(Expression node) in c:\PdcBuild\query\DLinq\Engine\Converter.cs:line 102……

 

LINQ非常酷

读完文档,I am in awe,虽然感觉这玩意带来的编程模型的变化会很大

对DLINQ的基于属性的做法

[Table(Name=”Customers”)]
public class Customer
{
 [Column(Id=true)]
 public string CustomerID;
 [Column]
 public string City;
}

不是很认同,因为这样把relational database schema与对象模型耦合在一起了,这也是我不喜欢Gentle.NET的原因

比较一下C#,VB的Query syntax,(更正,谢谢Ninputer)

IEnumerable<string> expr = from s in names 
                                           where s.Length == 5
                                           orderby s
                                           select s.ToUpper();

Dim expr As IEnumerable(Of String) = Select s.ToUpper() _
                                                            From s in names _
                                                            Where s.Length = 5 _
                                                            Order By s

感觉VB更自然些,也许VB将会成为“programming language of choice”?

New features in C# 3.0

Anders Hejlsberg接受Channel 9的采访,谈到如何把数据编程与对象编程统一起来

也参考

The LINQ Project

上面有 LINQ的技术预览,包括示范程序,白皮书文档,hands-on labs以及为使用LINQ技术编程所需的编译器支持,可以在Visual Studio 2005 下使用

MSDN上的C# 将来版本网站罗列了很多资源,包括,

C# 3.0 Language Specification

这篇blog提到了下面这些new features

http://blogs.sarkhouston.com/jrobertson/archive/2005/07/19/2742.aspx

Extension methods
Lambda expressions
Type inference and implicit types
Anonymous types
Expression Trees
Concurrency
Object Initializers
Dynamic Typing

9月22日MSDN将有个聊天活动,讨论C# 3.0的new features

C# 3.0 Language Enhancements

也做个比较(2)

对于

6。 整数交换

Java: 
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.out.println(“x = ” + x + “; y = ” + y);

C#:
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.Console.WriteLine(“x = ” + x + “; y = ” + y);

输出结果一样

Java x = 0; y = 1984
C# x = 0; y = 1984

fancyf 在上个post的回复中建议这个是Bug,其实不然,因为根据Specifications,

x ^= y ^= x ^= y;

其运算是这么做的(起码当前版本是如此):

“…The order in which operands in an expression are evaluated, is left to right. [Example: For example, in F(i) + G(i++) * H(i), method F is called using the old value of i, then method G is called with the old value of i, and, finally, method H is called with the new value of i. This is separate from and unrelated to operator precedence. end example]…” (见C# Language Specification, 14.2;至于Java中的表达式运算次序,参考 Java Language Specification, 15.7)

^=处,即对于 a ^= expr ,做运算时,a = a ^ expr,右边的a用的是运算expr的值,这样

y ^= x ^= y;  ==> x=1984^2001 =>17, y = 2001 ^ 17 => 1984
x ^= y ^= x ^= y; ==> x = 1984 ^ 1984 => 0

让我们再来看一下对应下面C#编码的IL码,观察stack的变化

public class CleverSwap {
    public static void Main(string[] args) {
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.Console.WriteLine(“x={0}, y={1}”,x,y);
    }
}

.method public hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       49 (0x31)
  .maxstack  4
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4     0x7c0  //stack: 0x7c0
  IL_0006:  stloc.0  //stack:  <==>x=0x7c0
  IL_0007:  ldc.i4     0x7d1 //stack: 0x7d1 
  IL_000c:  stloc.1  //stack:  <==>x=0x7c0,y=0x7d1
  IL_000d:  ldloc.0  //stack: 0x7c0 
  IL_000e:  ldloc.1  //stack: 0x7c0,0x7d1
  IL_000f:  ldloc.0  //stack: 0x7c0,0x7d1,0x7c0
  IL_0010:  ldloc.1  //stack: 0x7c0,0x7d1,0x7c0,0x7d1
  IL_0011:  xor   //stack: 0x7c0,0x7d1,0x11
  IL_0012:  dup   //stack: 0x7c0,0x7d1,0x11,0x11 
  IL_0013:  stloc.0  //stack: 0x7c0,0x7d1,0x11     <==>x=0x11,y=0x7d1
  IL_0014:  xor   //stack: 0x7c0,0x7c0     <==>x=0x11,y=0x7d1
  IL_0015:  dup   //stack: 0x7c0,0x7c0,0x7c0     <==>x=0x11,y=0x7d1
  IL_0016:  stloc.1  //stack: 0x7c0,0x7c0     <==>x=0x11,y=0x7c0
  IL_0017:  xor   //stack: 0x00     <==>x=0x11,y=0x7c0
  IL_0018:  stloc.0  //stack:  <==>x=0x00,y=0x7c0
  IL_0019:  ldstr      “x={0}, y={1}”
  IL_001e:  ldloc.0
  IL_001f:  box        [mscorlib]System.Int32
  IL_0024:  ldloc.1
  IL_0025:  box        [mscorlib]System.Int32
  IL_002a:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object,
                                                                object)
  IL_002f:  nop
  IL_0030:  ret
} // end of method CleverSwap::Main

至于C/C++,根据Joshua Bloch/Neal Gafter

“…… In C and C++, the order of expression evaluation is not specified. When compiling the expression x ^= expr, many C and C++ compilers sample the value of x after evaluating expr, which makes the idiom work. Although it may work, it runs afoul of the C/C++ rule that you must not modify a variable repeatedly between successive sequence points [ISO-C]. Therefore, the behavior of this idiom is undefined even in C and C++…..”

而Java/C#则明确规定了表达式中operand的evaluation次序,所以结果不同,起码当前版本是如此

也做个比较

Joshua Bloch 是Java语言组的设计师,去年离开Sun加盟Google,他的《Effective Java》一书在Java界影响很大。最近与Google的同事Neal Gafter (也是前Sun雇员) 合写了《Java Puzzlers: Traps, Pitfalls, and Corner Cases》。该书的几个条目以及全部Source Code可以在www.javapuzzlers.com下载到

我不想介入Java与C#间的比较,但还是不禁想比较一下里面的例子在C#里的行为。

下面是第二章里的几个例子的比较,是在.NET 2.0.50215 和Java 1.5.0下做的,至于结果为什么不一样,建议参考2门语言的Specifications。

1。奇偶性 

Java:

    public static boolean isOdd(int i) {
        return i % 2 == 1;
    }

C#:
    public static bool isOdd(int i) {
        return i % 2 == 1;
    }

输出结果是一样的,这里涉及负整数的余数的问题,但输出结果也许跟你想的也许不一样

isOdd i=-2 i=-1 i=0 i=1 i=2
Java false false false true false
C# False False False True False

2。浮点数的减法

Java: System.out.println(2.00 – 1.10);

C#: System.Console.WriteLine(2.00 – 1.10);

输出结果不一样

Java 0.8999999999999999
C# 0.9

对C#这个结果有点怀疑,大概是格式化的原因,因为如果用ILDASM看的话,是这样的

  IL_0000:  ldc.r8     0.89999999999999991
  IL_0009:  call       void [mscorlib]System.Console::WriteLine(float64)

3。大整数除法

Java:
        final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
        final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;

        System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);

C#:
        const long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
        const long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;

        System.Console.WriteLine(MICROS_PER_DAY / MILLIS_PER_DAY);

在C#里编译出错,使用unchecked后输出相同,但输出结果也许跟你想象的输出结果不一样

Java 5
C# 5

4。16位数的加法

Java: System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));

C#:  System.Console.WriteLine(“{0:x}”, 0x100000000L + 0xcafebabe);

输出结果不一样

Java cafebabe
C# 1cafebabe

5。多重转换

Java: System.out.println((int) (char) (byte) -1);

C#: 

 unchecked
 {
         System.Console.WriteLine((int) (char) (byte) -1);
 }

输出结果不一样

Java 65535
C# 255

6。 整数交换

Java: 
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.out.println(“x = ” + x + “; y = ” + y);

C#:
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.Console.WriteLine(“x = ” + x + “; y = ” + y);

输出结果一样

Java x = 0; y = 1984
C# x = 0; y = 1984

7。条件运算符

Java:
        char x = ‘X’;
        int i = 0;
        System.out.print(true  ? x : 0);
        System.out.print(false ? i : x);

C#:
        char x = ‘X’;
        int i = 0;
        System.Console.Write(true  ? x : 0);
        System.Console.Write(false ? i : x);

输出结果不一样

Java X88
C# 8888

CLR里的byref类型

看到同站博客linkcd很有趣的问题,虽有点直感,但因对CLR研究不深,所以Google了一下,看到了CLR开发组项目经理Joel Pobar的blog《CLR Type System notes》。他从Reflection的角度对ReferenceType,ValueType,Primitive (Scalar types),Enum,Array,ByRef,TypedRef,(unmanaged) Pointer,COMObject,Interface,TransparentProxy,Delegate等类型做了分析。跟问题有关的是其中的byref类型,对此,他是这么说的

“….

ByRef

ByRef types are managed pointers. ByRef don’t box and there are very few IL instruction that can be performed on ByRef types (i.e. ldind.ref, stind.ref). Because of that restriction there is never an instance of a ByRef type as far as reflection is concerned. However ByRef types are real, concrete types in the type system. Inspecting a method that takes a ByRef arg will reveal a unique type that is in no relationship with the type it represents (i.e. int& and int are in no relationship).

Reflection simulates ByRef and it’s the user responsibility to fetch the updated value out of the argument array used in reflection invocation. Verifiable IL ensures that ByRef types are only used in argument position. Usage of ByRef in any other location (return value, fields) results in unverifiable code.

Note:
Interesting features of ByRef

ChangeMyString(ref string s)

How do I discover via Reflection? When Reflection takes it as an argument (new Object[] myref) to late bound invoke a method that takes a reference, Reflection actually passes the object array, the method (invoked latebound) modifies the array reference instead of the actual ByRef variable that we wanted to modify…..”

翻了一下ECMA-335: CLI Partition I – Architecture的原文,第八章《Common Type System》对byref的定义:

“…..The byref constraint states that the content of the corresponding location is a managed pointer. A managed pointer may point to a local variable, parameter, field of a compound type, or element of an array…..”