技术开发 频道

NBearV3 Step by Step教程——IoC篇

    Step 3 设计实体、关系及元数据

    3.1 运行dist\ NBear.Tools.DbToEntityDesign.exe,在Connection String文本框中输入下面的连接子串:

Server=(local);Database=Northwind;Uid=sa;Pwd=sa

    我们将从SQL Server 2000自带的演示数据库Northwind,为我们需要的某些表和视图生成设计实体。

    点击Connect按钮,连接上数据库后,从左边的Tables和Views列表中分别选择下面这些表或视图(因为这里主要是演示,我们只选择和Category和Product相关的几个表和视图):Categories, Products, Products by Category。点击Generate Entities Design按钮,在代码生成文本框内的任意位置右键单击鼠标,并选择Copy to ClipBoard,这将能把所有的代码复制到剪贴板。

    3.2 为EntityDesigns工程添加到dist目录下的NBear.Common.Design.dll的引用,因为每一个设计实体接口必须继承自NBear.Common.Design.Entity这个接口。在EntitiyDesigns工程中新建一个代码文件EntityDesigns.cs,添加using System和using NBear.Common.Design设置namespace为EntityDesigns。并将刚才从DbToEntityDesign复制的代码粘贴至该文件中。
    此时,EntityDesigns.cs文件中的内容应该象下面这样:

using System; using NBear.Common.Design; namespace EntityDesigns { public interface Categories : Entity { [PrimaryKey] int CategoryID { get; } [SqlType("nvarchar(15)")] string CategoryName { get; set; } [SqlType("ntext")] string Description { get; set; } byte[] Picture { get; set; } } // }

    您会注意到,生成的代码中,数据库中的表或视图名称对应设计实体的名称,字段名称对应设计实体的属性名称,如果名称中包含空格,空格会被转换为_nbsp_。同时,是主键的标字段对应的属性,会自动包含PrimaryKey这个Attribute,而所有的文本类型的字段,则会自动包含从数据库中继承的数据类型和长度。另外,所有从视图生成的实体接口会包含ReadOnly这个Attribute,代表,该实体是一个只能用来从数据库取数据,不能用来保存数据到数据库的实体。

    3.3 下面,我们可以对这些生成的代码做一下改造,让我们看着更舒服。比如,用_nbsp_代表空格多少影响视觉审美,我们可以给设计实体添加MappingName这个Attribute,来修改实体接口名称,但是,保证实体还是对应数据库中的这个表或视图。例如,对于Products_nbsp_by_nbsp_Category视图,我们可以将它修改为下面的样子:

[ReadOnly] [MappingName("Products by Category")] public interface ProductsByCategory : Entity { [SqlType("nvarchar(15)")] string CategoryName { get; } [SqlType("nvarchar(40)")] string ProductName { get; } [SqlType("nvarchar(20)")] string QuantityPerUnit { get; } short UnitsInStock { get; } bool Discontinued { get; } }

    我们可能同样不喜欢实体名称是复数的英文单词,所以,我们也可以象下面这样把Categories实体的名称改为Category:

[MappingName("Categories")] public interface Category : Entity { [PrimaryKey] int CategoryID { get; } [SqlType("nvarchar(15)")] string CategoryName { get; set; } [SqlType("ntext")] string Description { get; set; } byte[] Picture { get; set; } }

     我们可以用同样的方法,修改所有的实体名称,和属性名称。属性名称同样支持MappingName这个Attribute。我们将所有的实体名称的复数改为单数,并去掉_nbsp_。现在,所有的设计实体应该象下面这个样子:

using System; using NBear.Common.Design; namespace EntityDesigns { [MappingName("Categories")] public interface Category : Entity { [PrimaryKey] int CategoryID { get; } [SqlType("nvarchar(15)")] string CategoryName { get; set; } [SqlType("ntext")] string Description { get; set; } byte[] Picture { get; set; } [Query(Where="{CategoryID} = @CategoryID", OrderBy="{ProductName}", LazyLoad=true)] [Contained] Product[] Products { get; set; } } [MappingName("Products")] public interface Product : Entity { [PrimaryKey] int ProductID { get; } [SqlType("nvarchar(40)")] string ProductName { get; set; } int SupplierID { get; set; } int CategoryID { get; set; } [SqlType("nvarchar(20)")] string QuantityPerUnit { get; set; } decimal UnitPrice { get; set; } short UnitsInStock { get; set; } short UnitsOnOrder { get; set; } short ReorderLevel { get; set; } bool Discontinued { get; set; } [Query(Where="{CategoryID} = @CategoryID", LazyLoad=false)] Category Category { get; set; } } [ReadOnly] [MappingName("Products by Category")] public interface ProductsByCategory : Entity { [SqlType("nvarchar(15)")] string CategoryName { get; } [SqlType("nvarchar(40)")] string ProductName { get; } [SqlType("nvarchar(20)")] string QuantityPerUnit { get; } short UnitsInStock { get; } bool Discontinued { get; } } }

    3.4 实体和属性名称我们改造完了,下面还可以给设计实体添加一点关联。我们可以注意到,Category和Product是一个明显的1对多关联。因此,我们可以像下面这样,为Category实体添加一个Products属性,1对多关联到Product表。

[MappingName("Categories")] public interface Category : Entity { [PrimaryKey] int CategoryID { get; } [SqlType("nvarchar(15)")] string CategoryName { get; set; } [SqlType("ntext")] string Description { get; set; } byte[] Picture { get; set; } [FkQuery("Category", OrderBy="{ProductName}", Contained=true, LazyLoad=true)] Product[] Products { get; set; } }

    如果您看过之前的ORM教程,您应该能理解加粗的代码的意思。它表示Category实体的CategoryID属性和Product实体的CategoryID关联,Products属性的Product按ProductName正序排序,同时,该属性延迟载入(即到第一次访问该属性才载入)。Contained同时代表Products属性在Category保存或删除时,会自动级联保存或删除。

    3.5 我们同时也可以给Product添加到Category的引用,因为,在查看一个Product信息时,查看相关的Category是非常常见的。注意,此时我们可以删掉Product中原来的CategoryID属性,将它合并到Category属性中:

[MappingName("Products")] public interface Product : Entity { [PrimaryKey] int ProductID { get; } [SqlType("nvarchar(40)")] string ProductName { get; set; } int SupplierID { get; set; } [SqlType("nvarchar(20)")] string QuantityPerUnit { get; set; } decimal UnitPrice { get; set; } short UnitsInStock { get; set; } short UnitsOnOrder { get; set; } short ReorderLevel { get; set; } bool Discontinued { get; set; } [FkReverseQuery(LazyLoad = true)] [MappingName("CategoryID")] Category Category { get; set; } }

    注意,我们没有添加ContainedAttribute,因为,我们并不希望一个Category跟随他的一个Product的更新级联更新。同时,我们将Category属性设置为非延迟载入(即实例化Product的同时就载入Category属性的数据)。

    这里要特别引起注意的是,当两个实体互相关联,或者,多个实体循环关联时,千万注意不要同时将互相关联的属性全都设为LazyLoad=fasle,否则将可能导致循环载入,造成程序死循环。在NBearV3的后续有版本将会引入缓存机制来解决这个问题,但是在这之前,请大家自己注意小心避免。

0
相关文章