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

Apress Expert C sharp 2005 (Phần 4) potx

50 283 0

Đ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 0,91 MB

Nội dung

Inside this method, the AddBusinessRules() method is called. Before that, however, the ValidationRules object needs to be given a reference to the business object so it can properly apply the validation rules to the properties. Finally, a virtual OnDeserialized method is invoked so the business developer can respond to the deserialization operation if desired. The ValidationRules object maintains a list of currently broken rules. This was used earlier in the implementation of the IsValid property, but there’s value in exposing the collection itself: [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Advanced)] public virtual Validation.BrokenRulesCollection BrokenRulesCollection { get { return ValidationRules.GetBrokenRules(); } } Within ValidationRules, this collection is implemented to be read-only. Even though the col- lection is exposed as a public property, it can’t be changed by the UI. However, the UI can display the list of broken rules to the user if so desired. System.ComponentModel.IDataErrorInfo Windows Forms data binding uses the IDataErrorInfo inter face to interrogate a data source for validation errors. This interface allows a data source, such as a business object, to provide human- readable descriptions of errors at the object and property levels. This information is used by grid controls and the ErrorProvider control to display error icons and tooltip descriptions . The ValidationRules object will provide a list of broken rules for each property on the object, making it relatively easy to implement IDataErrorInfo: string IDataErrorInfo.Error { get { if (!IsValid) return ValidationRules.GetBrokenRules().ToString(); else return String.Empty; } } string IDataErrorInfo.this[string columnName] { get { string result = string.Empty; if (!IsValid) { Validation.BrokenRule rule = ValidationRules.GetBrokenRules().GetFirstBrokenRule(columnName); if (rule != null) result = rule.Description; } return result; } } The Error pr operty returns a text value describing the validation errors for the object as a whole. The indexer returns a text value describing any validation error for a specific property. In this imple- mentation, only the first validation error in the list is returned. In either case, if there are no errors, an empty string value is returned—telling data binding that there are no broken rules to report. CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION124 6323_c03_final.qxd 2/27/06 1:23 PM Page 124 Authorization Rules In a manner similar to validation rules, authorization rules are managed by an AuthorizationRules object. The BusinessBase class collaborates with AuthorizationRules to implement authorization rules for each property. To simplify usage of this feature, BusinessBase encapsulates and abstracts the underlying behavior. Step one is to declare a field and property for the rules: [NotUndoable()] private Security.AuthorizationRules _authorizationRules; protected Security.AuthorizationRules AuthorizationRules { get { if (_authorizationRules == null) _authorizationRules = new Security.AuthorizationRules(); return _authorizationRules; } } BusinessBase also declares a virtual AddAuthorizationRules() method that the business devel- oper can override in a business class. The business developer should write code in this method to specify which r oles are allowed and denied access to read and write specific properties: protected virtual void AddAuthorizationRules() { } The BusinessBase constructor automatically calls AddAuthorizationRules() so any role-property relationships are established when the object is first created. The BusinessBase class also defines methods so both the business object developer and UI developer can find out whether the current user is allowed to read or write to a specific property. The CanReadProperty() methods indicate whether the user can r ead a specific pr operty, while the CanWriteProperty() methods do the same for altering a property. Both have several overloads. Only the CanReadProperty() methods will be shown here, and you can look at the CanWriteProperty() methods in the downloaded code. The primary CanReadProperty() implementation enforces the authorization rules for a property, making use of the AuthorizationRules object: [EditorBrowsable(EditorBrowsableState.Advanced)] public virtual bool CanReadProperty(string propertyName) { bool result = true; if (AuthorizationRules.HasReadAllowedRoles(propertyName)) { // some users are explicitly granted read access // in which case all other users are denied. if (!AuthorizationRules.IsReadAllowed(propertyName)) result = false; } else if (AuthorizationRules.HasReadDeniedRoles(propertyName)) { // some users are explicitly denied read access. if (AuthorizationRules.IsReadDenied(propertyName)) result = false; } return result; } CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION 125 6323_c03_final.qxd 2/27/06 1:23 PM Page 125 The AuthorizationRules object can maintain a list of roles explicitly granted access to a prop- erty, and a separate list of roles explicitly denied access. This algorithm first checks to see if there are any roles granted access, and if so, it assumes all other roles are denied. On the other hand, if no roles are explicitly granted access, it assumes all roles have access—except those in the denied list. Notice that the method is virtual, so a business developer can override this behavior to imple- ment a different authorization algorithm if needed. The CanWriteProperty() method operates in the s ame manner and is also v irtual . As with the PropertyHasChanged() method earlier in the chapter, the CanReadProperty() imple- mentation requires a string parameter indicating the property name. That forces the use of literal strings in the business object, which should be avoided for maintainability. To assist in this effort, there’s an overloaded version that uses System.Diagnostics to retrieve the property name, just like PropertyHasChanged(). There’s a third overload as well. Notice that the CanReadProperty() implementation returns a Boolean result, allowing the calling code to decide what to do if access is denied. That’s fine, but within a business object’s property, denied access will almost always trigger throwing a security exception. The final overload simplifies business object property code by throwing this exception automatically: [System.Runtime.CompilerServices.MethodImpl( System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] public bool CanReadProperty(bool throwOnFalse) { string propertyName = new System.Diagnostics.StackTrace(). GetFrame(1).GetMethod().Name.Substring(4); bool result = CanReadProperty(propertyName); if (throwOnFalse && result == false) throw new System.Security.SecurityException( String.Format("{0} ({1})", Resources.PropertyGetNotAllowed, propertyName)); return result; } This version of the method uses System.Diagnostics to retrieve the property name. But if access is denied, it optionally throws an exception. This allows code in a property to enforce property read and write authorization with just two lines of code and no string literals. The Boolean parameter to this method is only required to create a different method signature. Otherwise, the only difference would be the return type (or lack thereof), which isn’t sufficient for method overloading. System.ICloneable The BusinessBase class implements the System.ICloneable interface. This interface defines a Clone() method that can be called to create a clone, or copy, of an object. The Csla.Core.ObjectCloner class implements a general cloning solution that works against any serializable object, making it very easy to implement a Clone() method. H owever, there are cases in which a business developer might not want to return an e xact clone of an object. To accommodate this case, the cloning will be handled by a virtual method so that the business developer can override the method and replace the cloning mechanism with their own, if needed: object ICloneable.Clone() { return GetClone(); } CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION126 6323_c03_final.qxd 2/27/06 1:23 PM Page 126 [EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual object GetClone() { return ObjectCloner.Clone(this); } Notice that neither of these methods is public. The only way to invoke this Clone() method is through the ICloneable interface. Later in the chapter, BusinessBase<T> will implement a strongly typed public Clone() method by virtue of being a generic type. T he G etClone() m ethod is protected in scope to allow customization of the cloning process by a business developer. While a straight copy of the object is typically the required behavior, some- times a business object needs to do extra work when creating a clone of itself. ReadOnlyBindingList Class The final type in the Csla.Core namespace is the ReadOnlyBindingList<C> class. This implements a read-only collection based on System.ComponentModel.BindingList<T>. The standard BindingList<T> class implements a read-write collection that supports data binding, but there are numerous cases in which a read-only collection is useful. For example, ReadOnlyBindingList is the base class for Csla.ReadOnlyListBase, Csla.NameValueListBase, and Csla.Validation.BrokenRulesCollection. This class inherits from BindingList. It is also serializable and abstract, like all the framework base classes: [Serializable()] public abstract class ReadOnlyBindingList<C> : System.ComponentModel.BindingList<C>, Core.IBusinessObject { } All the basic collection and data binding behaviors are already implemented by BindingList. Making the collection read-only is a matter of overriding a few methods to prevent alteration of the collection. Of course, the collection has to be read-write at some point, in order to get data into the collection at all. To contr ol whether the collection is read-only or not, there’s a field and a property: private bool _isReadOnly = true; public bool IsReadOnly { get { return _isReadOnly; } protected set { _isReadOnly = value; } } Notice that while the IsReadOnly property is public for reading, it is protected for changing. This way, any code can determine if the collection is read-only or read-write, but only a subclass can lock or unlock the collection. The class contains a constr uctor that tur ns off the options to edit, r emo ve, or create items in the collection b y setting some properties in the BindingList base class: protected ReadOnlyBindingList() { AllowEdit = false; AllowRemove = false; AllowNew = false; } CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION 127 6323_c03_final.qxd 2/27/06 1:23 PM Page 127 The rest of the class overrides the methods in BindingList that control alteration of the collec- tion. Each override checks the IsReadOnly property and throws an exception when an attempt is made to change the collection when it is in read-only mode. The only complicated overrides are ClearItems() and RemoveItem(). This is because AllowRemove is typically set to false and must be temporarily changed to true to allow the operation (when the collection is not in read-only mode). For instance, here’s the ClearItems() method: protected override void ClearItems() { if (!IsReadOnly) { bool oldValue = AllowRemove; AllowRemove = true; base.ClearItems(); AllowRemove = oldValue; } else throw new NotSupportedException(Resources.ClearInvalidException); } The original AllowRemove value is restored after the oper ation is complete . This completes all the types in the Csla.Core namespace. The rest of the implementation is available in the code do wnload for the book. Let ’s move on and discuss the types in the Csla. Validation namespace. Csla.Validation Namespace The Csla.Validation namespace contains types that assist the business developer in implementing and enforcing business rules. The Csla.Core.BusinessBase class, discussed earlier in the “Business- Base Class” section, illustrated how some of the functionality in the Csla.Validation namespace will be used. This includes managing a list of business rules for each of the object’s properties and for maintaining a list of currently broken business rules. Obviously, the framework can’t implement the actual business rules and validation code—that will v ary from application to application. However, business rules follow a very specific pattern in that they are either broken or not. The result of a rule being checked is a Boolean value and a human- readable description of why the rule is broken. This makes it possible to check the rules and then maintain a list of broken rules—including human-readable descriptions of each rule. RuleHandler Delegate Given that rules follow a specific pattern, it is possible to define a method signature that covers virtually all business rules. In .NET, a method signature can be formally defined using a delegate; here’s the definition for a rule method: public delegate bool RuleHandler(object target, RuleArgs e); Every rule is implemented as a method that returns a Boolean result: true if the rule is satisfied, false if the r ule is br oken. The object containing the data to be v alidated is passed as the first argu- ment, and the second ar gument is a RuleArgs object that can be used to pass extr a r ule-specific information. This means that a business rule in a business class looks like this: CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION128 6323_c03_final.qxd 2/27/06 1:23 PM Page 128 private bool CustNameRequired(object target, RuleArgs e) { If (string.IsNullOrEmpty(((Customer)target).Name) { e.Description = "Customer name required"; return false; } else return true; } If the length of the target object’s Name property is zero, then the rule is not satisfied, so it returns false. It also sets the Description property of the RuleArgs object to a human-readable description of why the rule is broken. This illustrates a rule that would be implemented within a single business class. By using reflec- tion, it is possible to write entirely reusable rule methods that can be used by any business class. You’ll see some examples of this in the “Common Business Rules” section of Chapter 5 when I discuss the CommonRules class. RuleArgs Class The RuleHandler delegate specifies the use of the RuleArgs object as a parameter to every rule method. This follows the general pattern used throughout .NET of passing an EventArgs parameter to all event handlers. Business rules aren’t event handlers, so RuleArgs doesn’t inherit from EventArgs, but it fol- lows the same basic principal: public class RuleArgs { private string _propertyName; private string _description; public string PropertyName { get { return _propertyName; } } public string Description { get { return _description; } set { _description = value; } } public RuleArgs(string propertyName) { _propertyName = propertyName; } public override string ToString() { return _propertyName; } } The goal is to be able to pass data into and out of the r ule method in a clearly defined manner . At a minimum, RuleArgs passes the name of the property to be validated into the rule method, and passes back any broken rule description out of the rule method. To do this, it simply contains a read- only PropertyName property and a read-write Description property. CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION 129 6323_c03_final.qxd 2/27/06 1:23 PM Page 129 More important is the fact that the author of a rule method can create a subclass of RuleArgs to provide extra information. For instance, implementing a maximum value rule implies that the max- imum allowed value can be provided to the rule. To do this, the rule author would create a subclass of RuleArgs. You’ll see an example of this in the Common Business Rules section of Chapter 5 when I discuss the CommonRules class. RuleMethod Class T he V alidationRules c lass will maintain a list of rules for each property. This implies that ValidationRules has information about each rule method. This is the purpose of the RuleMethod class. It stores information about each rule, including the target object containing the data the rule should validate, a delegate reference to the rule method itself, a unique name for the rule, and any custom RuleArgs object that should be passed to the rule method. This information is stored in a set of fields with associated properties. The fields are declared like this: private object _target; private RuleHandler _handler; private string _ruleName = String.Empty; private RuleArgs _args; The RuleMethod class is scoped as internal, as it is used by other classes in the Csla.Validation namespace, but shouldn’t be used by code outside the framework. The unique r ule name associated with each rule is derived automatically by combining the name of the rule method with the string representation of the RuleArgs object. By default, this is the name of the property with which it is associated: _ruleName = _handler.Method.Name + "!" + _args.ToString(); Because the r ule name must be unique, any custom subclasses of RuleArgs should be sur e to override ToString() to return a value that includes any custom data that is part of the arguments object. When the business developer associates a rule method with a property, ValidationRules cre- ates a RuleMethod object to maintain all this information. This RuleMethod object is what’s actually associated with the property, thus providing all the information needed to invoke the rule when appropriate. In fact, the RuleMethod object handles the invocation of the rule method itself by exposing an Invoke() method: public bool Invoke() { return _handler.Invoke(_target, _args); } When ValidationRules is asked to check the business r ules , it merely loops through its list of RuleMethod objects, asking each one to invoke the rule it represents. As you can see, the Invoke() method simply invokes the method via the delegate reference, passing in a reference to the object to be validated (the business object) and the RuleArgs object associated with the rule. ValidationRules Class The ValidationRules class is the primary class in the Csla.Validation namespace. Every business object that uses the v alidation r ules functionality will contain its o wn ValidationRules object. ValidationRules relies on the other classes in Csla.Validation to do its work. Together, these classes maintain the list of rules for each property and the list of currently broken rules. CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION130 6323_c03_final.qxd 2/27/06 1:23 PM Page 130 Managing Rules for Properties You’ve already seen how a business rule is defined based on the RuleHandler delegate. A key part of what ValidationRules does is keep a list of such rule methods for each of the business object’s properties. Referencing the Business Object Remember that each rule method accepts a target parameter, which is the object containing the data to be validated. This target is always the business object, so ValidationRules keeps a reference to the business object. This reference is provided via the constructor and can be reset through the SetTarget() method—both of which you’ve seen in the implementation of Csla.Core.BusinessBase: [NonSerialized()] private object _target; internal ValidationRules(object businessObject) { SetTarget(businessObject); } internal void SetTarget(object businessObject) { _target = businessObject; } Notice that the _target field is marked as [NonSerialized()]. This is important because other- wise the BinaryFormatter would trace the circular reference between the business object and the ValidationRules object, causing a bloated serialization byte stream. No failure would result, but the size of the byte str eam would be lar ger than needed, which might cause a performance issue in some cases. Associating Rules with Properties To provide good performance in managing the list of rules for each property, ValidationRules uses an optimal data structure. Specifically, it has a dictionary with an entry for each property. Each entry in the dictionary contains a list of the rules for that property. This provides for very fast lookup to get the list of rules for a specific property, since the dictionary can jump right to the property’s entry. The dictionary is strongly typed, keyed by the property name, and used for storing strongly typed lists of RuleMethod objects: [NonSerialized()] private Dictionary<string, List<RuleMethod>> _rulesList; The business dev eloper calls an AddRule() method to associate a r ule method with a property on the business object. There are two versions of this method, the simplest accepting just a rule method delegate and the name of the property: public void AddRule(RuleHandler handler, string propertyName) { // get the list of rules for the property List<RuleMethod> list = GetRulesForProperty(propertyName); // we have the list, add our new rule list.Add(new RuleMethod(_target, handler, propertyName)); } CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION 131 6323_c03_final.qxd 2/27/06 1:23 PM Page 131 The GetRulesForProperty() method returns the list of RuleMethod objects associated with the property. If such a list doesn’t already exist, it creates an empty list and adds it to the dictionary. This is another example of lazy object creation. If there are no rules for a property, no list object is ever added to the dictionary, thus reducing the overhead of the whole process. In fact, the dictionary object itself is created on demand as well, so if no business rules are ever associated with properties for an object, even that little bit of overhead is avoided. T he other A ddRule() i mplementation provides an increased level of control. Its method signa- ture is as follows: public void AddRule(RuleHandler handler, RuleArgs args) This overload allows the business developer to provide a specific RuleArgs object that will be passed to the rule method when it is invoked. This is required for any rule methods that require cus- tom RuleArgs subclasses, so it will be used any time extra information needs to be passed to the rule method. The combination of the RuleMethod class, the dictionary and list object combination, and the AddRule() methods covers the management of the rules associated with each property. Checking Validation Rules Once a set of rule methods have been associated with the properties of a business object, there needs to be a way to invoke those rules. Typically, when a single property is changed on a business object, only the rules for that property need to be checked. A t other times, the rules for all the object’s prop - erties need to be checked. This is true when an object is first created, for instance, since multiple properties of the object could star t out with inv alid values. To cover these two cases, ValidationRules implements two CheckRules() methods. The first checks the rules for a specific property: public void CheckRules(string propertyName) { List<RuleMethod> list; // get the list of rules to check if (RulesList.ContainsKey(propertyName)) { list = RulesList[propertyName]; if (list == null) return; // now check the rules foreach (RuleMethod rule in list) { if (rule.Invoke()) BrokenRulesList.Remove(rule); else BrokenRulesList.Add(rule); } } } This method checks to see if the RulesList (the dictionar y) contains an entr y for the specified property. If so, it retrieves the list of RuleMethod objects and loops through them, asking each one to invoke its underlying rule method. If a rule returns true, then BrokenRulesList.Remove() is called to ensure that the rule isn’t listed as a br oken rule. If the rule returns false, then BrokenRulesList.Add() is called to ensure that the CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION132 6323_c03_final.qxd 2/27/06 1:23 PM Page 132 rule is listed as a broken rule. The BrokenRulesList class is part of the Csla.Validation namespace, and will be discussed shortly. The other CheckRules() implementation checks all the rules that have been added to the ValidationRules object: public void CheckRules() { // get the rules for each rule name foreach (KeyValuePair<string, List<RuleMethod>> de in RulesList) { List<RuleMethod> list = de.Value; // now check the rules foreach (RuleMethod rule in list) { if (rule.Invoke()) BrokenRulesList.Remove(rule); else BrokenRulesList.Add(rule); } } } This method simply loops through all items in the RulesList dictionary. Every entry in the dic- tionary is a list of RuleMethod objects, so it then loops through each list, invoking all the rules. The rule is then added or removed from BrokenRulesList based on the result. At this point, it should be clear how ValidationRules associates rule methods with properties and is then able to check those rules for a specific proper ty or for the business object as a whole. Maintaining a List of Broken Rules The ValidationRules object also maintains a list of currently broken validation rules. This list was used in the CheckRules() methods, and is declared as follows: private BrokenRulesCollection _brokenRules; private BrokenRulesCollection BrokenRulesList { get { if (_brokenRules == null) _brokenRules = new BrokenRulesCollection(); return _brokenRules; } } Notice that the _brokenRules field is not adorned with either the [NotUndoable()] or [NonSerialized()] attributes. The list of currently broken rules is directly part of a business object’s state, and so it is subject to n-level undo operations and to being transferred across the networ k along with the business object. This way , if a business dev eloper transfers an invalid object across the network or makes a clone, the object remains invalid, with its list of broken rules intact. The BrokenRulesList value is also exposed via a public method. To any external consumer, such as code in the UI, this is a read-only collection: CHAPTER 3 ■ BUSINESS FRAMEWORK IMPLEMENTATION 133 6323_c03_final.qxd 2/27/06 1:23 PM Page 133 [...]... _editLevel field to counter one call to CopyState() The method then cascades the AcceptChanges() call to each child object, so that child object can accept its own changes The only complex bit of code is that the “edit level added” value of each child must also be altered: void Core.IUndoableObject.AcceptChanges() { AcceptChanges(); } private void AcceptChanges() { // we are coming up one edit level _editLevel... _editLevel += 1; 6323 _c0 3_final.qxd 2/27/06 1:23 PM Page 151 CHAPTER 3 s BUSINESS FRAMEWORK IMPLEMENTATION // cascade the call to all child objects foreach (C child in this) child.CopyState(); // cascade the call to all deleted child objects foreach (C child in DeletedList) child.CopyState(); } There are technically two CopyState() methods—one for the Csla.Core.IUndoableObject interface and the other a... 0; // cascade the call to all child objects foreach (C child in this) { child.AcceptChanges(); // if item is below its point of addition, // lower point of addition if (child.EditLevelAdded > _editLevel) child.EditLevelAdded = _editLevel; } // cascade the call to all deleted child objects for (int index = DeletedList.Count - 1; index >= 0; index ) { C child = DeletedList[index]; child.AcceptChanges();... NotSupportedException(Resources.NoCancelEditChildException); UndoChanges(); } public void ApplyEdit() { if (this.IsChild) throw new NotSupportedException(Resources.NoApplyEditChildException); AcceptChanges(); } All three methods are very straightforward and allow developers to create a UI that starts editing a collection with BeginEdit(), lets the user interact with the collection, and then either cancels or accepts the changes... GetRolesForProperty(string propertyName, AccessType access) { RolesForProperty currentRoles = GetRolesForProperty(propertyName); switch (access) { case AccessType.ReadAllowed : return currentRoles.ReadAllowed.ToArray(); case AccessType.ReadDenied : return currentRoles.ReadDenied.ToArray(); case AccessType.WriteAllowed : return currentRoles.WriteAllowed.ToArray(); case AccessType.WriteDenied : return currentRoles.WriteDenied.ToArray();... its children 143 6323 _c0 3_final.qxd 144 2/27/06 1:23 PM Page 144 CHAPTER 3 s BUSINESS FRAMEWORK IMPLEMENTATION Root and Child Behaviors The idea that a collection can be a root object or a child object is particularly important It’s fairly obvious that a collection can be a child object—an Invoice root object will have a LineItems collection that contains LineItem objects, so the LineItems collection... Csla.Core.IEditableCollection interface, which inherits from Csla.Core.IUndoableObject Implementing the interface requires that the class implement CopyState(), UndoChanges(), and AcceptChanges() methods that store and restore the collection’s state as appropriate Because a collection can also be a root object, it needs public methods named BeginEdit(), CancelEdit(), and ApplyEdit(), like BusinessBase In either scenario,... the collection, but the collection still has access to them if needed After the CancelEdit() call, the collection’s edit level goes back to 0 Since child A came from the database, it was “added” at edit level 0, so it sticks around Child B, on the other hand, was added at edit level 1, so it goes away Also, child A has its state reset as part of the CancelEdit() call (remember that CancelEdit() causes... [EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual object GetClone() { return Core.ObjectCloner.Clone(this); } public T Clone() { return (T)GetClone(); } The ICloneable.Clone() and strongly typed Clone() methods delegate to GetClone() to do their work Other than the data access functionality that will be added in Chapter 4, this concludes the functionality for the BusinessListBase class ReadOnlyBase Class With BusinessBase... a common occurrence Windows Forms 2.0 uses a new interface, ICancelAddNew, that is implemented by BindingList This interface notifies the collection that the child should be removed, rather than notifying the child object itself The code in the RemoveItem() method takes care of the ICancelAddNew case automatically, so this code is really here to support backward compatibility for anyone explicitly . 141 6323 _c0 3_final.qxd 2/27/06 1:23 PM Page 141 Clone Method Earlier in the chapter, I discussed the ICloneable interface and the concept of cloning. The Csla. Core.ObjectCloner class contains code. changing. This way, any code can determine if the collection is read-only or read-write, but only a subclass can lock or unlock the collection. The class contains a constr uctor that tur ns off. deny): [EditorBrowsable(EditorBrowsableState.Advanced)] public string[] GetRolesForProperty(string propertyName, AccessType access) { RolesForProperty currentRoles = GetRolesForProperty(propertyName); switch (access) { case AccessType.ReadAllowed

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