1. Trang chủ
  2. » Công Nghệ Thông Tin

Apress Expert C sharp 2005 (Phần 9) ppsx

50 510 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 50
Dung lượng 751,23 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The Factory Methods region will contain the static factory methods to create or retrieve the object, along with the static delete method if the object is an editable root object.. These

Trang 1

Note If you’re calling a remote data portal, you must avoid object designs that require IDisposable.

Alternatively, you can modify the SimpleDataPortalclass to explicitly call Dispose()on your business objects on the server

Business Class Structure

As you’ve seen, business objects follow the same sequence of events for creation, retrieval, andupdates Because of this, there’s a structure and a set of features that are common to all of them.Although the structure and features are common, however, the actual code will vary for each busi-ness object Due to the consistency in structure, however, there’s great value in providing somefoundations that make it easier for the business developer to know what needs to be done

Also, there are differences between editable and read-only objects, and between root and childobjects After discussing the features common to all business objects, I’ll create “templates” to illus-trate the structure of each type of business object that you can create based on CSLA NET

The Serializable Attribute

All business objects must be unanchored so that they can move across the network as needed Thismeans that they must be marked as serializable by using the [Serializable()] attribute, as shownhere:

Common Regions

When writing code in VS NET, the #region directive can be used to place code into collapsible regions.This helps organize the code, and allows you to look only at the code pertaining to a specific type offunctionality

Trang 2

All business collection classes will have a common set of regions, as follows:

Trang 3

The Business Methods region will contain the methods that are used by UI code (or other client

code) to interact with the business object This includes any properties that allow retrieval or ing of values in the object, as well as methods that operate on the object’s data to perform businessprocessing

chang-The Validation Rules region will contain the AddBusinessRules() method, and any custom rule

methods required by the object

The Authorization Rules region will contain the AddAuthorizationRules() method It will also

contain a standard set of static methods indicating whether the current user is authorized to get,add, save, or delete this type of business object

The Factory Methods region will contain the static factory methods to create or retrieve the

object, along with the static delete method (if the object is an editable root object) It will alsocontain the default constructor for the class, which must be scoped as non-public (i.e., private

or protected) to force the use of the factory methods when creating the business object

Trang 4

The Data Access region will contain the DataPortal_XYZ methods It will also contain the Criteria

class used to create, retrieve, or delete the object

Your business objects may require other code that doesn’t fit neatly into these regions, and youshould feel free to add extra regions if needed But these regions cover the vast majority of code

required by typical business objects, and in most cases they’re all you’ll need

Private Default Constructor

All business objects will be implemented to make use of the class-in-charge scheme discussed in

Chapter 1 Factory methods are used in lieu of the new keyword, which means that it’s best to

pre-vent the use of new, thereby forcing the UI developer to use the factory methods instead

The data portal mechanism, as implemented in Chapter 4, requires business classes to include

a default constructor As I reviewed the create, fetch, update, and delete processes for each type of

object earlier in this chapter, each sequence diagram showed how the server-side data portal created

an instance of the business object This is done using a technique that requires a default constructor

By making the default constructor private or protected (and by not creating other public structors), you ensure that UI code must use the factory methods to get an instance of any object:

The Csla.CriteriaBase approach is intended primarily for use with code-generation tools

The Criteria class simply contains the data that’s required to identify the specific object to

be retrieved or the default data to be loaded Since it’s passed by value to the data portal, this class

must be marked as [Serializable()]

Tip Technically, the Criteriaclass can have any name, as long as it’s [Serializable()], and is either

nested in the business class or inherits from CriteriaBase Some objects may have more than one criteria class,

each one defining a different set of criteria that can be used to retrieve the object

Since this class is no more than a way to ferry data to the data portal, it doesn’t need to befancy Typically, it’s implemented with a constructor to make it easier to create and populate the

object all at once For example, here’s a Criteria class that includes an EmployeeID field:

Trang 5

private string _employeeId;

public string EmployeId {

get { return _employeeId; } }

public Criteria(string employeeId) { _employeeId = employeeId; } }

private string _employeeId;

public string EmployeId{

get { return _employeeId; }}

public Criteria(string employeeId)

: base(typeof(MyBusinessClass))

{ _employeeId = employeeId; }}

//

All Criteria classes are constructed using one of these two schemes Nested criteria classesare scoped as private because they are only needed within the context of the business class TheCriteriaBase class is typically used by code-generation tools, in which case the class is typicallyprotected in scope so that it is available to subclasses as well

Note Code generation is outside the scope of this book For good information on code generation, including the rationale behind CriteriaBase, please refer to Kathleen Dollard’s book, Code Generation in Microsoft NET(Apress, 2004)

Even though the Criteria object is passed through the data portal, it’s passed as a type object,

so the DataPortal code doesn’t need access to the object’s code This is ideal, because it means that

UI developers, or other business object developers, won’t see the Criteria class, thus improving thebusiness object’s overall encapsulation

Trang 6

The Criteria classes shown thus far include a constructor that accepts the criteria data value.

This is done to simplify the code that will go into the static factory methods Rather than forcing

the business developer to create a Criteria object and then load its values, this constructor allows

the Criteria object to be created and initialized in a single statement In many cases, this means

that a static factory method will contain just one line of code! For instance:

public static Project GetProject(Guid id)

collection in which you’re directly retrieving a collection of child objects, the Criteria class may not

define a single object, but rather act as a search filter that returns the collection populated with all

matching child objects

In other cases, an object may have no criteria data at all In that case, a Criteria class is stillrequired, but it would be empty:

business object to be retrieved This is typically used when retrieving a root collection object for

which you want all the child objects in the database returned at all times I’ll use this technique

to create the ProjectList and ResourceList collection classes in Chapter 8

Class Structures

At this point in the chapter, I’ve walked through the life cycle of typical business objects, so you

know the sequence of events that will occur as they are created, retrieved, updated, and deleted

I’ve also discussed the code concepts and structures that are common to all business classes Now

let’s dive in and look at the specific coding structure for each type of business class that you can

create based on the CSLA NET framework These include the following:

• Editable root

• Editable child

• Editable, “switchable” (i.e., root or child) object

• Editable root collection

• Editable child collection

• Read-only object

• Read-only collection

• Command object

• Name/value listFor each of these object types, I’ll create the basic starting code that belongs in the class

In a sense, these are the templates from which business classes can be built

Trang 7

Tip You can use this code to create either snippets or class templates for use in Visual Studio The Csla\Snippetssubdirectory in the code download (available from www.apress.com) contains a set of sample snippets you may find valuable.

Editable Root Business Objects

The most common type of object will be the editable root business object, since any object-orientedsystem based on CSLA NET will typically have at least one root business object or root collection.(Examples of this type of object include the Project and Resource objects discussed in Chapter 8.)These objects often contain collections of child objects, as well as their own object-specific data

As well as being common, an editable object that’s also a root object is the most complex objecttype, so its code template covers all the possible code regions The basic structure for an editableroot object, with example or template code in each region, is as follows:

public int id {

get { CanReadProperty(true);

return _id;

} set { CanWriteProperty(true);

if (_id != value) {

_id = value;

PropertyHasChanged();

} } } protected override object GetIdValue() {

// TODO: add validation rules //ValidationRules.AddRule(null, "");

}

Trang 8

#region Authorization Rules protected override void AddAuthorizationRules() {

// TODO: add authorization rules //AuthorizationRules.AllowWrite("", "");

} public static bool CanAddObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return true;

} public static bool CanGetObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return true;

} public static bool CanEditObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return true;

} public static bool CanDeleteObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return DataPortal.Create<EditableRoot>();

} public static EditableRoot GetEditableRoot(int id) {

return DataPortal.Create<EditableRoot>(new Criteria(id));

} public static void DeleteEditableRoot(int id) {

DataPortal.Delete(new Criteria(id));

}

Trang 9

private EditableRoot() { /* Require use of factory methods */ }

get { return _id; } }

public Criteria(int id) { _id = id; }

} private void DataPortal_Create(Criteria criteria) {

// TODO: load default values }

private void DataPortal_Fetch(Criteria criteria) {

// TODO: load values }

protected override void DataPortal_Insert() {

// TODO: insert values }

protected override void DataPortal_Update() {

// TODO: update values }

protected override void DataPortal_DeleteSelf() {

DataPortal_Delete(new Criteria(_id));

} private void DataPortal_Delete(Criteria criteria) {

// TODO: delete values }

#endregion

}

You must define the class, including making it serializable, giving it a name, and having it inheritfrom BusinessBase

Trang 10

The Business Methods region includes all member or instance field declarations, along with any

business-specific properties and methods These properties and methods typically interact with the

instance fields, performing calculations and other manipulation of the data based on the business

logic

Notice the GetIdValue() method, which is required when inheriting from BusinessBase Thismethod should return a unique identifying value for the object The value is directly returned by the

default ToString() method in BusinessBase, and is used in the implementation of the Equals() and

GetHashCode() methods as well For details, refer to Chapter 3

The Validation Rules region, at a minimum, overrides the AddBusinessRules() method In this

method, you call ValidationRules.AddRule() to associate rule methods with properties This region

may also include custom rule methods for rules that aren’t already available in Csla.Validation

CommonRules or in your own library of rule methods

The Authorization Rules region overrides the AddAuthorizationRules() method and implements

a set of static authorization methods

The AddAuthorizationRules() method should include calls to methods on the AuthorizationRules object: AllowRead(), AllowWrite(), DenyRead(), and DenyWrite() Each

one associates a property with a list of roles that are to be allowed read and write access to that

property

The static authorization methods are CanGetObject(), CanAddObject(), CanEditObject(), andCanDeleteObject() These methods should check the current user’s roles to determine whether the

user is in a role that allows or denies the particular operation The purpose of these methods is so

the UI developer can easily determine whether the current user can get, add, update, or delete this

type of object That way, the UI can enable, disable, or hide controls to provide appropriate visual

cues to the end user

Since these are static methods, there’s no way to make them part of the BusinessBase class,and they must be directly declared and implemented in each business class

In the Factory Methods region, there are static factory methods to create, retrieve, and delete

the object Of course, these are just examples that must be changed as appropriate The parameters

accepted and Criteria object used must be tailored to match the identifying criteria for your ular business object

partic-Finally, the Data Access region includes the Criteria class and the DataPortal_XYZ methods.

These methods must include the code to load defaults, retrieve object data, update object data, and

delete object data, as appropriate In most cases, this will be done through ADO.NET, but this code

could just as easily be implemented to read or write to an XML file, call a web service, or use any

other data store you can imagine

The [RunLocal()] attribute is for objects that do not load default values from the database

when they are created The use of the [RunLocal()] attribute on DataPortal_Create() is optional,

and is used to force the data portal to always run the method locally When this attribute is used,

the DataPortal_Create() method should not access the database, because it may not be running

in a physical location where the database is available

The [Transactional()] attributes on the methods that insert, update, or delete data specifythat those methods should run within a System.Transactions transactional context You may opt

instead to use the TransactionTypes.EnterpriseServices setting to run within a COM+ distributed

transaction, or TransactionTypes.Manual to handle your own transactions using ADO.NET

Tip Many organizations use an abstract, metadata-driven data access layer In environments like this, the

business objects don’t use ADO.NET directly This works fine with CSLA NET, since the data access code in the

DataPortal_XYZmethods can interact with an abstract data access layer just as easily as it can interact with

ADO.NET directly

Trang 11

The key thing to note about this code template is that there’s very little code in the class that’snot related to the business requirements Most of the code implements business properties, valida-tion, and authorization rules or data access The bulk of the nonbusiness code (code not specific

to your business problem) is already implemented in the CSLA NET framework

Immediate or Deferred Deletion

As implemented in the template, the UI developer can delete the object by calling the static deletemethod and providing the criteria to identify the object to be deleted Another option is to imple-

ment deferred deletion, whereby the object must be retrieved, marked as deleted, and then updated

in order for it to be deleted The object’s data is then deleted as part of the update process

To support deferred deletion, simply remove the static delete method:

// public static void DeleteEditableRoot(int id)

Editable Child Business Objects

Most applications will have some editable child objects, or even grandchild objects Examples of theseinclude the ProjectResource and ResourceAssignment objects In many cases, the child objects are con-tained within a child collection object, which I’ll discuss later In other cases, the child object might bereferenced directly by the parent object Either way, the basic structure of a child object is the same; insome ways, this template is very similar to the editable root:

public int id {

get { CanReadProperty(true);

return _id;

} set { CanWriteProperty(true);

if (_id != value) {

_id = value;

PropertyHasChanged();

} } }

Trang 12

protected override object GetIdValue() {

// TODO: add validation rules //ValidationRules.AddRule(null, "");

}

#endregion

#region Authorization Rules protected override void AddAuthorizationRules() {

// TODO: add authorization rules //AuthorizationRules.AllowWrite("", "");

}

#endregion

#region Factory Methods internal static EditableChild NewEditableChild() {

// TODO: change to use new keyword if not loading defaults return DataPortal.Create<EditableChild>();

} internal static EditableChild GetEditableChild(SqlDataReader dr) {

return new EditableChild(dr);

} private EditableChild() {

MarkAsChild();

} private EditableChild(SqlDataReader dr) {

Trang 13

protected override void DataPortal_Create(object criteria) {

// TODO: load default values, or remove method }

private void Fetch(SqlDataReader dr) {

// TODO: load values MarkOld();

} internal void Insert(object parent) {

// TODO: insert values MarkOld();

} internal void Update(object parent) {

// TODO: update values MarkOld();

} internal void DeleteSelf() {

// TODO: delete values MarkNew();

The Business Methods region is the same as with a root object: it simply implements the erties and methods required by the business rules Similarly, the Validation Rules region is the same

prop-as with a root object

The Authorization Rules region is simpler, as it only implements the AddAuthorizationRules()

method Control over retrieving, adding, updating, and deleting child objects is controlled by theparent object or collection, so no static methods are needed here for that purpose

The Factory Methods region is a bit different The factory methods are internal rather than

public, as they should only be called by the parent object, not by the UI code Also, there’s no needfor a static delete method because BusinessBase implements a DeleteChild() method that is auto-matically called by BusinessListBase when the child is removed from a collection

Notice that the NewEditableChild() method invokes the data portal to create the child object.This allows the child object to load itself with default values from the database when it is created I’ll discuss an alternative approach that avoids using the database shortly

The GetEditableChild() method uses the new keyword to create an instance of the child object.See how it accepts a data reader as a parameter and passes it to the constructor The idea is that theparent object will have already retrieved the necessary data from the database and is providing it tothe child object through this parameter That parameterized constructor then calls a Fetch() method

in the Data Access region where the object loads its data.

Trang 14

If you are using a data store other than a relational database, the data reader parameter would

be replaced by some other type of object For instance, if the object’s data is being loaded from an

XML document, the parameter would likely be an XmlNode that contains the child object’s data

The biggest difference from a root object comes in the Data Access region The DataPortal_

Create() method is implemented to support the loading of default values from the database on

the creation of a new child object, but no other DataPortal_XYZ methods are implemented

Instead, there’s a private Fetch() method to load the object with data, and internal methodsnamed Insert(), Update(), and DeleteSelf() to handle insert, update, and delete operations These

mirror the functionality of the DataPortal_XYZ methods, but they are called by the parent object

rather than by the data portal

Notice that Insert() and Update() both accept a reference to the parent object as a parameter Theassumption is that any child object will need data from the parent while being inserted or updated into

the database Most often, the parent contains a foreign key value required by the child object during

data access

Note Typically, the parentparameter will be strongly typed based on the class of the parent object itself

As an example, the ProjectResource child object will need the Id property from its parentProject object so that it can store it as a foreign key in the database By getting a reference to its

parent Project object, the ProjectResource gains access to that value as needed

The Fetch(), Insert(), and Update() methods all call MarkOld() when they are done, becausethe object’s data in memory matches that in the database at those points, so the object is neither

new nor dirty The DeleteSelf() method calls MarkNew() as it completes, because the object’s

pri-mary key value is not in the database at that point, so the object qualifies as a new object.

Object Creation Without Defaults

As implemented, the template uses DataPortal.Create() to load the child object with default values

from the database As discussed earlier, if the object doesn’t need to load default values from the

database, the code can be implemented more efficiently by changing the static factory method

to create the child object directly:

internal static EditableChild NewEditableChild()

It’s possible that some classes must be instantiated as root objects on some occasions and as child

objects on others This can be handled by conditionally calling MarkAsChild(), based on how the object

is being created

Note In most cases, the need for a switchable object indicates a flawed object model While there are exceptions

for which this makes sense, you should carefully examine your object model to see if there’s a simpler solution before

implementing a switchable object

Trang 15

Conditionally calling MarkAsChild() typically can’t be done in the default constructor, because

there’s no way to determine whether the object is being created as a root or a child object at that point

Instead, you need to go back to your object’s life cycle to see where you can make this decision In fact,

since the default is for an object to be a root object, all you need to do is determine the paths by which

a child object can be created, and make sure to call MarkAsChild() only in those cases.

The template for creating a “switchable” object is the same as the editable root template, withthe following exceptions:

• Dual criteria objects

• Dual create and fetch factory methods

• Dual create and fetch data access methodsLet’s discuss each change in turn

Dual Criteria Classes

The object’s criteria must now include a flag to indicate whether the object is being created as a root

or a child object (this is in addition to any object-specific criteria fields in this class) This can be doneeither by adding an actual flag field to the criteria class, or by creating a second criteria class I pre-fer the second approach as it makes the code simpler overall

Remember that for a child object, the criteria class is only used for the create operation, and so

it typically doesn’t need any actual criteria data The result is that there are two criteria classes; forexample:

get { return _id; } }

public RootCriteria(int id) { _id = id; }

public RootCriteria() { }

}

[Serializable()]

private class ChildCriteria

{ }

These two classes will be used to differentiate the way the object should be created

Dual Factory Methods

Instead of single factory methods to create and retrieve the object, there will be two methods foreach operation: one public, the other internal

public static Switchable NewSwitchable()

{ return DataPortal.Create<SwitchableObject>(

new RootCriteria());

}

Trang 16

internal static SwitchableObject NewSwitchableChild() {

return DataPortal.Create<SwitchableObject>(

new ChildCriteria());

} public static SwitchableObject GetSwitchableRoot(int id) {

return DataPortal.Create<SwitchableObject>(

new RootCriteria(id));

} internal static SwitchableObject GetSwitchableChild(

SqlDataReader dr) {

return new SwitchableObject(dr);

}

Notice how the NewSwitchable() methods are each designed The public version (used to ate a root object) uses the RootCriteria object, while the internal version (called by a parent object

cre-to create a child object) uses ChildCriteria The DataPortal_Create() methods, which follow, are

called based on the type of the criteria object

The two GetSwitchable() methods are even more different The public one is called by UI code

to retrieve a root object In this case, the data portal is called to retrieve the object based on the

sup-plied criteria The internal one follows the pattern for child objects, accepting a data reader from

the parent object and passing it along to a private constructor, which in turn calls a private Fetch()

method

Dual Data Access Methods

The data access methods that handle create and fetch operations are different for a root and child

object Because of this, these methods are duplicated in a switchable object In most cases, they can

delegate to a shared implementation that is private to the class For instance:

private void DataPortal_Create(RootCriteria criteria)

Trang 17

Similarly, the data retrieval operations are duplicated:

private void DataPortal_Fetch(RootCriteria criteria)

{

// TODO: create data reader to load values using (SqlDataReader dr = null)

{ DoFetch(dr);

} } private void Fetch(SqlDataReader dr) {

MarkAsChild();

DoFetch(dr);

} private void DoFetch(SqlDataReader dr) {

// TODO: load values }

If the object is being loaded from the UI, then it is treated as a root object and DataPortal_Fetch() is called, passing in appropriate criteria This method opens the database, and sets up andexecutes a database command object to get back a data reader That data reader is then passed to

a central DoFetch() helper method to copy the data from the data reader into the object’s fields

On the other hand, if the object is being loaded from a parent object as a child, then its terized constructor is called, which in turn calls the Fetch() method This method calls MarkAsChild()

parame-to mark the object as a child, and then the DoFetch() helper is called parame-to copy the data from the datareader into the object’s fields

Object Creation Without Defaults

When creating the object using the new keyword instead of calling DataPortal.Create(), theinternal factory method can directly call MarkAsChild(), as shown here:

internal static SwitchableObject NewSwitchableChild()

Editable Root Collection

At times, applications need to retrieve a collection of child objects directly To do this, you need to ate a root collection object For instance, the application may have a Windows Forms UI consisting of

cre-a Dcre-atcre-aGridView control thcre-at displcre-ays cre-a collection of Contcre-act objects If the root object is cre-a collection

of child Contact objects, the UI developer can simply bind the collection to the DataGridView, and theuser can do in-place editing of the objects within the grid

This approach means that all the child objects are handled as a single unit in terms of dataaccess They are loaded into the collection to start with, so the user can interact with all of them,

Trang 18

and then save them all at once when all edits are complete This is only subtly different from having

a regular root object that has a collection of child objects Figure 7-10 shows the regular root object

approach on the left, and the collection root object approach on the right

This approach isn’t recommended when there are large numbers of potential child objects,because the retrieval process can become too slow, but it can be very useful in cases where you can

specify criteria to limit the number of objects returned To create an editable root collection object,

use a template like this:

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return true;

} public static bool CanGetObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return true;

} public static bool CanEditObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return true;

} Figure 7-10.Comparing simple root objects (left) and collection root objects (right)

Trang 19

public static bool CanDeleteObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return new EditableRootList();

} public static EditableRootList GetEditableRootList(int id) {

return DataPortal.Fetch<EditableRootList>(new Criteria(id)); }

private EditableRootList() { /* Require use of factory methods */ }

get { return _id; } }

public Criteria(int id) { _id = id; }

} private void DataPortal_Fetch(Criteria criteria) {

RaiseListChangedEvents = false;

// TODO: load values using (SqlDataReader dr = null) {

while (dr.Read()) {

this.Add(EditableChild.GetEditableChild(dr));

} } RaiseListChangedEvents = true;

}

Trang 20

protected override void DataPortal_Update() {

else item.Update(this);

RaiseListChangedEvents = true;

}

#endregion

}

The Authorization Rules region contains the standard static methods discussed earlier for

editable root objects Since collection objects don’t have detailed properties, there’s no need or

support for the AddAuthorizationRules() method

The Factory Methods region implements factory methods to create, retrieve, and (optionally)

delete the collection The create method simply uses the new keyword to create an instance of the

collection There’s no need to load default values for the collection itself The retrieve and delete

methods rely on the data portal to do much of the work, ultimately delegating the call to the

appropriate DataPortal_XYZ method

In the Data Access region, the DataPortal_Fetch() method is responsible for getting the data

from the database, typically via a data reader It then calls the static factory method of the child

class for each row in the data reader, thereby allowing each child object to load its data The static

factory method in the child class calls its own private constructor to actually load the data from

the data reader

The DataPortal_Update() method must loop through all the child objects contained in thedeleted object collection, calling each object’s DeleteSelf() method in turn An alternative is to have

the collection object dynamically generate a SQL statement to delete all the items in the DeleteList

with a single call The specific implementation is up to the business developer and may vary

depend-ing on the database design

Once the child objects have been deleted from the database, that list is cleared Then the activechild objects are either inserted or updated based on their IsNew property value

Note It’s critical that the deleted child objects be processed first.

It’s quite possible for the user to delete a child object from the collection, and then add a new

child object with the same primary key value This means that the collection will have the original

child object marked as deleted in the list of deleted child objects, and the new child object in the

list of active objects This new object will have its IsNew property set to true, because it’s a new

object If the original child object isn’t deleted first, the insertion of the new child object will fail

Trang 21

Thus, the code first processes the list of deleted child objects, and then moves on to process thelist of active child objects.

Both the DataPortal_Fetch() and DataPortal_Update() methods set the RaiseListChanged➥Events property to false before changing the collection, and then restore it to true once the opera-tion is complete Setting this property to false tells the base BindingList<T> class to stop raising the ListChanged event When doing batches of updates or changes to a collection, this can increaseperformance

Editable Child Collection

The most common type of collection is one that is contained within a parent object to manage

a collection of child objects for that parent; like ProjectResources and ResourceAssignments in thesample application

Tip Note that the parent object here might be a root object, or it might be a child itself—child objects can benested, if that’s what the business object model requires In other words, this concept supports not only root-child,but also child-grandchild and grandchild-to-great-grandchild relationships

A child collection class inherits from BusinessListBase and calls MarkAsChild() during its ation process to indicate that it’s operating in child mode This also means that it won’t be directlyretrieved or updated by the DataPortal, but instead will be retrieved or updated by its parent object:

return new EditableChildList();

} internal static EditableChildList GetEditableChildList(

SqlDataReader dr) {

return new EditableChildList(dr);

} private EditableChildList() {

MarkAsChild();

} private EditableChildList(SqlDataReader dr) {

Trang 22

private void Fetch(SqlDataReader dr) {

RaiseListChangedEvents = false;

while (dr.Read()) {

this.Add(EditableChild.GetEditableChild(dr));

} RaiseListChangedEvents = true;

} internal void Update(object parent) {

else item.Update(parent);

RaiseListChangedEvents = true;

}

#endregion }

As you can see, this code is very similar to a root collection in structure The differences start withthe factory methods Since only a parent object can create or fetch an instance of this class, the static

factory methods are scoped as internal The static method to create an object simply returns a new

collection object As with the EditableChild template, the constructor calls MarkAsChild() to indicate

that this is a child object

Likewise, the static method to load the child collection with data creates a new collection objectand then calls a parameterized constructor just like in the EditableChild template That constructor

calls a Fetch() method to load the data

The Update() method is identical to the DataPortal_Update() method in the EditableRootList

It loops through the list of deleted child objects, calling their DeleteSelf() methods, and then loops

through the active child objects, calling Insert() or Update() as appropriate

Notice, however, that the Update() method accepts a reference to the parent object as a eter, and this value is provided to the child objects’ Insert() and Update() methods As discussed

param-earlier, this allows the child objects to use data from the parent object as needed for things like

for-eign key values and so forth

Read-Only Business Objects

Sometimes, an application may need an object that provides data in a only fashion For a only list of data, there’s ReadOnlyListBase; but if the requirement is for a single object containing

read-read-only data, it should inherit from ReadOnlyBase This is one of the simplest types of object to

create, since it does nothing more than retrieve and return data, as shown here:

Trang 23

// TODO: add your own fields, properties and methods private int _id;

public int Id {

get { CanReadProperty(true);

return _id;

} } protected override object GetIdValue() {

// TODO: add authorization rules //AuthorizationRules.AllowRead("", "");

} public static bool CanGetObject() {

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return DataPortal.Fetch<ReadOnlyRoot>(new Criteria(id)); }

private ReadOnlyRoot() { /* require use of factory methods */ }

Trang 24

public int Id {

get { return _id; } }

public Criteria(int id) {

_id = id;

} } private void DataPortal_Fetch(Criteria criteria) {

// TODO: load values _id = criteria.Id;

}

#endregion }

Like other business objects, a read-only object will have instance fields that contain its data

It will typically also have read-only properties or methods that allow client code to retrieve values

As long as they don’t change the state of the object, these may even be calculated values

Like editable objects, read-only objects must override the GetIdValue() method and provide

a unique identifying value for the object This value is used by the Equals(), GetHashCode(), and

ToString() implementations in the ReadOnlyBase class If those implementations are inadequate

for your needs, you can override them and provide your own implementations

The AddAuthorizationRules() method only needs to add roles for read access, since no erties should be implemented to allow altering of data It also includes a CanGetObject() method

prop-so the UI can enable or disable options based on that result

In the Factory Methods region, there’s just one factory method that retrieves the object by calling

DataPortal.Fetch() This means there’s also a Criteria class, which should be modified to contain

the criteria data needed to select the correct object for retrieval

The Data Access region just contains DataPortal_Fetch() Of course, there’s no need to support

updating or deleting a read-only object

Read-Only Collections of Objects

Applications commonly retrieve read-only collections of objects The CSLA NET framework

includes the ReadOnlyListBase class to help create read-only collections It throws an exception any

time there’s an attempt to change which items are in the collection by adding or removing objects

Note The template shown here is for the most common scenario: a read-only root collection You can adapt

this to provide a read-only child collection if desired

However, there’s no way for the collection object to stop client code from interacting with thechild objects themselves Typically, the items in the collection will expose only read-only proper-

ties and methods If read-write objects are put into the collection, client code will be able to alter

their data A read-only collection only guarantees that objects can’t be added or removed from the

collection

Trang 25

The child objects may be derived from ReadOnlyBase, but more often they will be simple objectsthat don’t inherit from any CSLA NET base class The only requirements for these child objects is thatthey are implemented with read-only properties and that they are marked as [Serializable()].The code for a typical read-only collection object looks like this:

// TODO: customize to check user role //return ApplicationContext.User.IsInRole("");

return DataPortal.Fetch<ReadOnlyList>(new Criteria(filter));

} private ReadOnlyList() { /* require use of factory methods */ }

private string _filter;

public string Filter {

get { return _filter; } }

public Criteria(string filter) {

_filter = filter;

} } private void DataPortal_Fetch(Criteria criteria) {

RaiseListChangedEvents = false;

IsReadOnly = false;

// load values using (SqlDataReader dr = null)

Ngày đăng: 06/07/2014, 00:20

TỪ KHÓA LIÊN QUAN