还记得用户注册时收集信息的方式吗?下面这几句:
//......
AddressInfo address = addr.Address;
//.....
AccountInfo accountInfo = new AccountInfo(userId, password, email, address, language, favCategory, showFavorites, showBanners);
//.....
ccountController.CreateAccount(accountInfo)
AddressInfo和AccountInfo都是瘦实体类,整个程序都用这种模型来完成信息传递。这些类的定义都在Model组件中。应该是整个petshop3.0中最重要的组件之一。
Model:瘦数据类或业务实体,在应用程序各层之间传递数据的瘦数据类。这些是用 C# 类实现的,每个字段都以属性的形式公开。 每个类都标记为“serializable”,启用进程间传输。
.net是面向对象的,这大家都知道,可以在具体实现的时候应该怎么做呢?总之我是完全的不明白。不过看了petshop3.0中Model组件的应用,算是又长了见识。
在程序中总有那么一些信息可以归为一类,比如商品信息,用户信息……拿用户信息来说,应该有用户名、密码、住址、电话、个人爱好等具体的信息。在petshop3.0,这些就可以总归为一个实体,而其中的信息都是这个类的属性,在其中住址、电话等信息又可以单独的归为一个实体,因为他们是描述个人具体信息的,并且是可变的(描述的不是很准确,可能有错误)。所以我们看到petshop3.0把这些信息归成一个AddressInfo实体,而该实体又成为AccountInfo实体的一个属性。具体应用的时候,这些实体都是作为一个整体使用,因为你总不能把一个人切开吧?再从c#的角度看,这些都是类型,可以看成用户的自定义类型,所以我们看到AddressUI这个用户控件把AddressInfo做为属性对外公开。
实体模型的使用贯穿了整个petshop3.0,从表面看,几乎每一层都引用了Model组件。还是以用户这块来说下去,表示层收集数据的时候,会把用户填的信息作为一个实体来收集,完成后用户接口处理组件会把信息实体做为参数传送给业务逻辑组件,业务逻辑部分再把信息实体做为参数经工厂传送给数据处理组件,在这里做最后处理,提交到数据库或返回错误。
在整个过程中,根本就不存在单独的值类型的传递。这样做的好处很明显,使逻辑操作更加透明和易于理解,便于构建复杂系统的模型,可以封装一些不需要向程序公开的信息,等等。
此类代码都处都是,一个比较明显的例子就是CartItemInfo,该实体模型表述放到购物车内的商品。该实体并不保存到数据库,完全是存在于进程中的。由于该实体的存在,大大简化了购物车这一抽象实体的组成。
实际上,在Model组件中根本就没有Cart这个实体。Cart的实现由BLL组件来完成。这个Cart并非和上面一样的瘦实体类,而是另外一种实体模型,因为它不仅包含着作为实体本身的信息,而且还有自己的行为。按照msdn上的叫法,这是“带有CRUD行为的业务实体”。我们可以把这个Cart看成一个智能机器人,他会自己挑选商品,并给出现在车内的信息。
通过类图来对比一下Cart和CartItemInfo。
CartItemInfo只有属性,而Cart则包括属性和方法。
要注意的是,Cart归属于业务逻辑组件,这是因为它的作用更多的是执行其行为,它的最主要的属性就是一个索引器,通过这个来获得CartItemInfo。
最后看一下CartItemInfo的代码,了解此类模型的基本构成。
/// <summary>
/// Business entity used to model items in a shopping cart
/// </summary>
[Serializable]
public class CartItemInfo {
private const string YES = "Yes";
private const string NO = "No";
// Internal member variables
private int _quantity = 1;
private bool _inStock = false;
private string _itemId = null;
private string _name;
private decimal _price;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="itemId">Every cart item requires an itemId</param>
public CartItemInfo(string itemId) {
this._itemId = itemId;
}
/// <summary>
/// Constructor with specified initial values
/// </summary>
/// <param name="itemId">Id of item to add to cart</param></param>
/// <param name="name">Name of item</param>
/// <param name="inStock">Is the item in stock</param>
/// <param name="qty">Quantity to purchase</param>
/// <param name="price">Price of item</param>
public CartItemInfo(string itemId, string name, bool inStock, int qty, decimal price) {
this._itemId = itemId;
this._name = name;
this._quantity = qty;
this._price = price;
this._inStock = inStock;
}
// Properties
public int Quantity {
get { return _quantity; }
set { _quantity = value; }
}
public bool InStock {
get { return _inStock; }
set { _inStock = value; }
}
public decimal Subtotal {
get { return (decimal)(this._quantity * this._price); }
}
public string ItemId {
get { return _itemId; }
}
public string Name {
get { return _name; }
}
public decimal Price {
get { return _price; }
}
}
下面是msdn上关于业务实体的一些资料。顺便转过来好了:)
----原文地址------
表示业务实体的自定义类通常包含以下成员:
图 9 所示为使用自定义实体类的方法。注意,实体类并不知道数据访问逻辑组件或基础数据库;所有数据库访问都由数据访问逻辑组件执行,以集中数据访问策略和业务逻辑。此外,在层间传递业务实体数据的方式与表示业务实体的格式也没有直接关系;例如,可以在本地将业务实体表示为对象,而用另一种方法(如标量值或 XML)将业务实体数据传递到其他层。
图 9:自定义业务实体组件的作用(单击缩略图以查看大图像)
在实现自定义实体组件时,请考虑以下建议:
有关说明如何表示自定义实体中数据的集合和层次结构的代码示例,请参阅附录中的如何表示自定义实体中数据的集合和层次结构。
有关说明如何将自定义实体绑定到用户界面控件的代码示例,请参阅附录中的如何将业务实体组件绑定到用户界面控件。
您可以对业务实体执行 XML 序列化而无需在实体中实现任何附加代码。然而,只有对象中的公共字段和公共读/写属性被序列化为 XML。专用字段、索引生成器、专用属性、只读属性及对象图不会被序列化。您可以使用自定义实体中的属性控制结果 XML。有关将自定义实体组件序列化为 XML 格式的详细信息,请参阅附录中的如何将业务实体组件序列化为 XML 格式。
格式类将序列化对象的所有公共和专用字段及属性。BinaryFormatter 将对象序列化为二进制格式,SoapFormatter 将对象序列化为 SOAP 格式。使用 BinaryFormatter 的序列化比使用 SoapFormatter 的序列化速度要快。要使用任何一个格式类,都必须将实体类标记为 [Serializable] 属性。如果需要显式控制序列化格式,您的类还必须实现 ISerializable 接口。有关如何使用格式序列化的详细信息,请参阅附录中的如何将业务实体组件序列化为二进制格式及如何将业务实体组件序列化为 SOAP 格式。
注意:还原序列化某个对象时,不会调用默认的构造函数。对还原序列化添加这项约束,是出于性能方面的考虑。
定义自定义实体的优点如下:
// 创建一个 ProductDALC 对象 ProductDALC dalcProduct = new ProductDALC(); // 使用该 ProductDALC 对象创建和填充一个 ProductEntity 对象。 // 此代码假设 ProductDALC 类有一个名为 GetProduct 的方法, // 该方法使用 Product ID 作参数(本例中为 21),并返回一个 // 包含该产品的所有数据的 ProductEntity 对象。 ProductEntity aProduct = dalcProduct.GetProduct(21); // 更改该产品的产品名称 aProduct.ProductName = "Roasted Coffee Beans";
在上述示例中,产品是一个名为 ProductEntity 的自定义实体类的一个实例。ProductDALC 类有一个名为 GetProduct 的方法,后者创建一个 ProductEntity 对象,将某个特定产品的数据填充到该对象,然后返回 ProductEntity 对象。调用应用程序可以使用 ProductName 等属性访问 ProductEntity 对象中的数据,并且可以调用方法以操作该对象。
// 调用一个在 ProductEntity 类中定义的方法。 aProduct.IncreaseUnitPriceBy(1.50);
在上述示例中,调用应用程序对 ProductEntity 对象调用一个名为 IncreaseUnitPriceBy 的方法。在调用应用程序对 ProductDALC 对象调用相应方法,从而将 ProductEntity 对象保存到数据库之前,这一更改并不是永久性的。
定义自定义实体的缺点如下:
在定义一个自定义实体时,可以提供方法以完全封装对基础数据访问逻辑组件的 CRUD 操作。这是比较传统的面向对象的方法,可能适用于复杂的对象域。客户端应用程序不再直接访问数据访问逻辑组件类,而是创建一个实体组件并对该实体组件调用 CRUD 方法。这些方法将调用基础的数据访问逻辑组件。
图 10 所示为带有 CRUD 行为的自定义实体类的作用。
图 10:带有 CRUD 行为的自定义业务实体组件的作用(单击缩略图以查看大图像)
定义带有 CRUD 行为的自定义实体类的优点如下:
定义带有 CRUD 行为的自定义实体类的缺点如下: