在读取数据库列时,我们常常都需要左令人厌烦的DBNull或null检验,这中代码很乏味.使用可空类型,就可以避免编写DBNull或null检验.可空类型的定义方式有两种:可以通过泛型类型Nullable显示定义,也可以将简介表姐?作为后缀加在数据类型上.
可空类型可以使用空结合运算符??进行检验. int? x = y ?? 0
有了可空类型,就可以将数据库中的空字段直接赋值给实体类中的可空字段或属性了.这里有个小问题,即如何知道是否将某个实例可空化.简单的档案室,所有值类型的字段和属性都能被做成可空点的.
[Database(Name = "Northwind")] public class Northwind : DataContext { private static readonly string connectionString = @"Server=localhost;Database=Northwind;Trusted_Connection=True;"; public Northwind() : base(connectionString) { } } [Table(Name = "Orders")] public class Order { [Column(IsPrimaryKey = true)] public int OrderID { get; set; } [Column()] public string CustomerID { get; set; } /// <summary>可空运算符</summary> [Column(CanBeNull = true)] public int? EmployeeID { get; set; } /// <summary>显示可空类型</summary> [Column(CanBeNull = true)] public Nullable<DateTime> OrderDate { get; set; } [Column(CanBeNull = true)] public DateTime? RequiredDate { get; set; } [Column(CanBeNull = true)] public DateTime? ShippedDate { get; set; } [Column(CanBeNull = true)] public int? ShipVia { get; set; } [Column(CanBeNull = true)] public decimal? Freight { get; set; } [Column(CanBeNull = true)] public string ShipName { get; set; } [Column(CanBeNull = true)] public string ShipAddress { get; set; } [Column(CanBeNull = true)] public string ShipCity { get; set; } [Column(CanBeNull = true)] public string ShipRegion { get; set; } [Column(CanBeNull = true)] public string ShipPostalCode { get; set; } [Column(CanBeNull = true)] public string ShipCountry { get; set; } public override string ToString() { StringBuilder builder = new StringBuilder(); PropertyInfo[] props = this.GetType().GetProperties(); // using array for each Array.ForEach(props.ToArray(), prop => builder.AppendFormat("{0} : {1}", prop.Name, prop.GetValue(this, null) == null ? "<empty>\n" : prop.GetValue(this, null).ToString() + "\n")); return builder.ToString(); } } class Program { static void Main(string[] args) { Northwind northwind = new Northwind(); Table<Order> orders = northwind.GetTable<Order>(); northwind.Log = Console.Out; Array.ForEach(orders.ToArray(), t => Console.WriteLine(t)); } }继承是面向对象编程领域中的一个概念,很容易被过分使用.深的继承层次结构将使架构难以理解,而且也很难用.对于多少继承"过多"这个问题,并没有一个较好的准则.
如何在持久化状态中表示继承层次结构
LINQ to SQL 支持单表映射方式.架设有一个积累和两个子类.单表映射就是说,一个表用于这三个类的全部持久化属性的并集(且没有重复).为了便于标明表示的是哪个类,需要使用一些键.对于未被某个特定类所使用的列,需要为该列赋null值.单表映射的性能很好,因为它在获取数据时不用执行行联接或多表扫描.
假设你想要为医院定义一个含有各种人员的层次结构.你可能会定义Person, Patient, VendorContact类.然后,再进一步架设Person含有一个唯一标识符以及姓和名.Patient可能会含有性别,年龄和上次到访日期,VendorContact可能会含有称谓和公司编号.
单映射表架构定义
属性名称数据类型是否为空PersonIDintUnckeckedKindKeychar(1)UnckeckedFirstNamenvarchar(15)UnckeckedLastNamenvarchar(15)UnckeckedGendernchar(1)CheckedAgeintCheckedDateOfLastVisitdatetimeCheckedTitlenvarchar(50)CheckedCompanyIDintCheckedCkecked和Unckecked值表示设计器中Allow nulls列的值.注意,除了基类中的列之外,其他的列均允许为null.这样做的原因是,对于数据行所表示的不同的类,由于某些行可能有也可能没有意义,因此这样做可以提高灵活性.此外,KindKey是一个类型标识符,指示当前行所代表的类的类型.
下面的例子演示的继承映射.基类上除了添加TableAttribute之外,还添加了InheritanceMappingAttribute.InheritanceMappingAttribute指示数据行所能表示的全部可能类型.命名参数code映射到积累中的鉴别器(Column(IsDiscriminator=true))代码值上.鉴别器用于指示当前数据行所表示的对象类型.
每个子类都会继承基类上的这些标签.ColumnAttribute用于映射对应的列,在整个继承层次结构中,某个列之需在任意一个类中出现一次即可.当你从DataContext获取表时,得到的将是单个映射表.LINQ将会读取Person表和鉴别器,然后指出Person, Patient和VendorContact继承层次结构中的具体类型,并将其添加到行集合中.也就是说,从DataContact中轻轨Table,LINQ将会根据鉴别器来判断它究竟应该创建Person, Patient还是VendorContact对象.
[Database(Name = "Inheritance")] public class Inheritance : DataContext { private static readonly string connectionString = "Server=localhost;Database=Northwind;Trusted_Connection=True;"; public Inheritance() : base(connectionString) { this.Log = Console.Out; } public Table<Patient> GetPatients() { return this.GetTable<Patient>(); } public Table<VendorContact> GetVendors() { return this.GetTable<VendorContact>(); } } [Table(Name = "Person")] [InheritanceMapping(Code = "P", Type = typeof(Person), IsDefault = true)] [InheritanceMapping(Code = "A", Type = typeof(Patient))] [InheritanceMapping(Code = "V", Type = typeof(VendorContact))] public class Person { [Column(IsPrimaryKey = true)] public int PersonID { get; set; } [Column(IsDiscriminator = true)] public char KindKey { get; set; } [Column()] public string FirstName { get; set; } [Column()] public string LastName { get; set; } public override string ToString() { StringBuilder builder = new StringBuilder(); builder.AppendFormat("Type: {0}\n", this.GetType().Name); PropertyInfo[] props = this.GetType().GetProperties(); // using array for each Array.ForEach(props.ToArray(), prop => builder.AppendFormat("{0} : {1}", prop.Name, prop.GetValue(this, null) == null ? "<empty>\n" : prop.GetValue(this, null).ToString() + "\n")); return builder.ToString(); } } public class Patient : Person { [Column()] public char Gender { get; set; } [Column()] public int Age { get; set; } [Column(CanBeNull = true)] public DateTime? DateOfLastVisit { get; set; } } public class VendorContact : Person { [Column()] public string Title { get; set; } [Column()] public int CompanyID { get; set; } } class Program { static void Main(string[] args) { Inheritance inherit = new Inheritance(); Table<Person> people = inherit.GetTable<Person>(); Array.ForEach(people.Where(person => person.GetType() == typeof(Patient)).ToArray(), r => Console.WriteLine(r)); } }用面向对象的话来说,关联(Association)表示的是,类A引用了类B,当并不会控制其声明中期.在C#中,任何使用内部类型的属性都可以称为一个关联.我们可以这样认为,由于CE有来及回收机制,因此没有什么东西是独立的,也说就是说,任何东西只要不是继承那么就是一个关联.由于C#能够创建类的实例,因此我们可以将含有类成员的那些类看作是通过复合而够承的聚合体. EntitySet是一个泛型类型,其中T是根据需要而加载的实体类的类型.EntitySet用于表示一个复合体,其中,一个类拥有一个或多个下级类.例如,客户拥有他们自己的订单,因此可以定义一个属性,其类型是EntitySet.访问这个EntitySet属性时,LINQ就会从这个EntitySet中加载数据.
下面的例子含有一个针对Northwind数据库的类型化DataContext, 其中还有一个映射Customers表的类(使用TableAttribute和ColmunAttribute).它还在具体的EntitySet对象上引入了AssociationAttribute.命名参数Storage指示存储Entity数据的字段,命名参数OtherKey指示关联父行和子行的外键.