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

Apress Expert C sharp 2005 (Phần 10) ppt

50 314 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

Nội dung

Notice how the code directly alters the instance fields of the object. For instance, the _id field is set to a new Guid value. Since the Id property is read-only, this is the only way to load the Id prop- erty with a new value. While the Started property is read-write and could be set through the property, it is more efficient and consistent to directly set the _started field. Since not all properties can be set, it is best to be consistent and always set fields directly. Additionally, the ValidationRules.CheckRules() call will apply all the validation rules in the entire o bject. Setting a property causes the validation rules for that property to be checked, so setting property values would cause validation rules to be run twice, which is wasteful. Setting the fields and then calling CheckRules() means validation rules are run only once. Of course, the default values set in a new object might not conform to the object’s validation rules. In fact, the Name property starts out as an empty string value, which means it is invalid, since that is a required property. Remember that this was specified in the AddBusinessRules() method by associating this property with the StringRequired rule method. To ensure that all validation rules are run against the newly created object’s data, ValidationRules.CheckRules() is called. Calling this method with no parameters causes it to run all the validation rules associated with all properties of the object, as defined in the object’s AddBusinessRules() method. The end result is that the new object has been loaded with default values, and those values have been validated. The new object is then returned by the data portal to the factory method ( NewProject() in this case), which typically returns it to the UI code. DataPortal_Fetch More interesting and complex is the DataPortal_Fetch() method, which is called by the data portal to tell the object that it should load its data from the database (or other data source). The method accepts a Criteria object as a parameter, which contains the criteria data needed to identify the data to load: private void DataPortal_Fetch(Criteria criteria) { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "getProject"; cm.Parameters.AddWithValue("@id", criteria.Id); using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader())) { dr.Read(); _id = dr.GetGuid("Id"); _name = dr.GetString("Name"); _started = dr.GetSmartDate("Started", _started.EmptyIsMin); _ended = dr.GetSmartDate("Ended", _ended.EmptyIsMin); _description = dr.GetString("Description"); dr.GetBytes("LastChanged", 0, _timestamp, 0, 8); // load child objects dr.NextResult(); _resources = ProjectResources.GetProjectResources(dr); } } } } CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION424 6323_c08_final.qxd 2/26/06 10:02 PM Page 424 This method is not marked with either the [RunLocal()] or [Transactional()] attributes. Since it does interact with the database, [RunLocal()] is inappropriate. That attribute could prevent the data portal from running this code on the application server, causing runtime errors when the data- base is inaccessible. Also, since this method doesn’t update any data, it doesn’t need transactional protection, and so there’s no need for the [Transactional()] attribute. You should also notice that no exceptions are caught by this code. If the requested Id value d oesn’t exist in the database, the result will be a SQL exception, which will automatically flow back through the data portal to the UI code, contained within a DataPortalException. This is intentional, as it allows the UI to have full access to the exception’s details so the UI can decide how to notify the user that the data doesn’t exist in the database. The first thing the method does is open a connection to the database: using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); Database.PTrackerConnection is a call to a helper class in ProjectTracker.Library. This helper simply abstracts the process of retrieving the database connection string. It uses System.Configuration to get the data, and looks like this: public static string PTrackerConnection { get { return ConfigurationManager.ConnectionStrings ["PTracker"].ConnectionString; } } Because the ConfigurationManager is used in this code , a reference to System.Configuration.dll is required by ProjectTracker.Library. This PTrackerConnection property is merely a convenience to simplify the code in business objects. You may use a similar concept in your code if you choose. Then, within a using block, a SqlCommand object is initialized to call the getProject stored procedure: using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "getProject"; cm.Parameters.AddWithValue("@id", criteria.Id); Note the use of the criteria parameter. This is the Criteria object that was created in the GetProject() factory method, and so it provides access to the criteria data supplied to the factory method by the UI. The SqlCommand object is then executed to return a data reader: using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader())) Rather than using a SqlDataReader, this code creates an instance of the Csla.Data.SafeData➥ Reader class. This provides automatic protection from errant null values in the data, and also enables suppor t for the SmartDate data type . The data reader is then used to populate the object’s fields like this: _id = dr.GetGuid("Id"); _name = dr.GetString("Name"); _started = dr.GetSmartDate("Started", _started.EmptyIsMin); _ended = dr.GetSmartDate("Ended", _ended.EmptyIsMin); _description = dr.GetString("Description"); dr.GetBytes("LastChanged", 0, _timestamp, 0, 8); CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 425 6323_c08_final.qxd 2/26/06 10:02 PM Page 425 The SmartDate values are retrieved using the SafeDataReader object’s GetSmartDate() method, which automatically handles the translation of null values into appropriate empty date values. Also notice that the LastChanged column is retrieved and placed into the _timestamp byte array. This value is never exposed outside the object, but is maintained for later use if the object is updated. Recall from Chapter 6 that LastChanged is a timestamp value in the database table, and is used by the updateProject stored procedure to implement first-write-wins optimistic concurrency. The object m ust be able to provide u pdateProject w ith the original t imestamp v alue that was in the table when the data was first loaded. At this point, the Project object’s fields have been loaded. But Project contains a collection of child objects, and they need to be loaded as well. Remember that the getProject stored procedure returns two result sets: the first with the project’s data; the second with the data for the child objects. The NextResult() method of the data reader moves to the second result set so the child collection object can simply loop through all the rows, creating a child object for each: dr.NextResult(); _resources = ProjectResources.GetProjectResources(dr); Now that the object contains data loaded directly from the database, it is an “old” object. The definition of an old object is that the primary key value in the object matches a primary key value in the database. In Chapter 4, the data portal was implemented to automatically call the object’s MarkOld() method after DataPortal_Fetch() is complete. That ensures that the object’s IsNew and IsDirty properties will return false. DataPortal_Insert The DataPortal_Insert() method handles the case in which a new object needs to insert its data into the database. It is invoked by the data portal as a result of the UI calling the object’s Save() method when the object’s IsNew property is true. As with all the methods that change the database, this one is marked with the [Transactional()] attribute to ensure that the code is transactionally protected: [Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_Insert() { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandText = "addProject"; DoInsertUpdate(cm); } } // update child objects _resources.Update(this); } As with DataPortal_Fetch(), this method opens a connection to the database and creates a SqlCommand object. However, it turns out that both the addProject and updateProject stored pro- cedur es take almost the same set of par ameters . To consolidate code, a DoInsertUpdate() helper method is called to load the common par ameters and to execute the SqlCommand object. That method looks like this: CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION426 6323_c08_final.qxd 2/26/06 10:02 PM Page 426 private void DoInsertUpdate(SqlCommand cm) { cm.CommandType = CommandType.StoredProcedure; cm.Parameters.AddWithValue("@id", _id); cm.Parameters.AddWithValue("@name", _name); cm.Parameters.AddWithValue("@started", _started.DBValue); cm.Parameters.AddWithValue("@ended", _ended.DBValue); cm.Parameters.AddWithValue("@description", _description); SqlParameter param = new SqlParameter("@newLastChanged", SqlDbType.Timestamp); param.Direction = ParameterDirection.Output; cm.Parameters.Add(param); cm.ExecuteNonQuery(); timestamp = (byte[])cm.Parameters["@newLastChanged"].Value; } The DataPortal_Insert() method already set the stored procedure name on the SqlCommand object, so this helper method only needs to add parameters to the object, loading it with the object’s data. It then executes the stored procedure. Recall from Chapter 6 that both the addProject and updateProject stored procedures perform a SELECT statement to return the updated LastChanged column value. This value is read as a result of the stored procedure call so that the object can update the _timestamp field with the new value from the database. As with DataPortal_Fetch(), the object needs to have the current value of the timestamp for any future updates to the database. Back in DataPortal_Insert(), once the insert operation is complete, the Project object’s data is in the database. However, a Project contains child objects, and their data must be added to the database as well. This is handled by calling an Update() method on the child collection object: _resources.Update(this); This method is scoped as internal and is intended for use only by the Project object. It loops through all the child objects in the collection, inserting each one into the database.You’ll see the code for this Update() method later in the chapter. Once DataPortal_Insert() is complete, the data portal automatically invokes the MarkOld() method on the object, ensuring that the IsNew and IsDirty properties are both false. Since the object’s primary key value in memory now matches a primary key value in the database, it is not new ; and since the rest of the object’s data values match those in the database, it is not dirty. DataPortal_Update The DataPortal_Update() method is very similar to DataPortal_Insert(), but it is called by the data portal in the case that IsNew is false. It too opens a database connection and creates a SqlCommand object, and then calls DoInsertUpdate() to execute the updateProject stor ed pr ocedur e: [Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_Update() { if (base.IsDirty) { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 427 6323_c08_final.qxd 2/26/06 10:02 PM Page 427 { cm.CommandText = "updateProject"; cm.Parameters.AddWithValue("@lastChanged", _timestamp); DoInsertUpdate(cm); } } } // update child objects _resources.Update(this); } However, the updateProject stored procedure requires one extra parameter not required by addProject: the timestamp value for the LastChanged column: cm.Parameters.AddWithValue("@lastChanged", _timestamp); This is required for the first-write-wins optimistic concurrency implemented by the stored pr ocedure. The goal is to ensure that multiple users can’t overwrite each other’s changes to the data. Other than adding this one extra parameter to the SqlCommand object, the DataPortal_Update() method is very similar to DataPortal_Insert(). DataPortal_DeleteSelf The final method that the data portal may invoke when the UI calls the object’s Save() method is DataPortal_DeleteSelf(). This method is invoked if the object’s IsDeleted property is true and its IsNew pr oper ty is false. In this case , the object needs to delete itself from the database. Remember that there are two ways objects can be deleted: through immediate or deferred deletion. Deferred deletion is when the object is loaded into memory, its IsDeleted property is set to true, and Save() is called. Immediate deletion is when a factory method is called and passes criteria identifying the object to the DataPortal.Delete() method. In the case of immediate deletion, the data portal ultimately calls DataPortal_Delete(), pass- ing the Criteria object to that method so it knows which data to delete. Deferred deletion calls DataPortal_DeleteSelf(), passing no Criteria object because the object is fully populated with data already. ■Note Implementing the DataPortal_DeleteSelf() method is only required if your object supports deferred deletion. In the Project object, deferred deletion is not supported, but I am implementing the method anyway to illustrate how it is done. The simplest way to implement DataPortal_DeleteSelf() is to create a Criteria object and delegate the call to DataPortal_Delete(): [Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_DeleteSelf() { DataPortal_Delete(new Criteria(_id)); } Y ou might wonder why the data por tal couldn ’ t do this for you automatically. But remember that the data portal has no idea what values are required to identify your business object’s data. Even if you assume that GetIdValue() returns the complete primary key value for the object, there’s no automatic way by which the data portal can create and properly initialize the specific Criteria object for every business object you might create. Thus, you must create the Criteria object and pass it to DataPortal_Delete(). CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION428 6323_c08_final.qxd 2/26/06 10:02 PM Page 428 DataPortal_Delete The final data portal method is DataPortal_Delete(). This method is called from two possible sources—if immediate deletion is used, the UI will call the static deletion method, which will call DataPortal_Delete(); and if deferred deletion is used, then DataPortal_Delete() is called by DataPortal_DeleteSelf(). A Criteria object is passed as a parameter, identifying the data to be deleted. Then it’s just a matter of calling the deleteProject stored procedure as follows: [Transactional(TransactionalTypes.TransactionScope)] private void DataPortal_Delete(Criteria criteria) { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "deleteProject"; cm.Parameters.AddWithValue("@id", criteria.Id); cm.ExecuteNonQuery(); } } } The method just opens a database connection, configures a SqlCommand object to call the deleteProject stored procedur e, and executes the command. In the downloaded code, you’ll also see a code region for an Exists command, which I’ll discuss later in the chapter. ProjectResources A Project object contains a collection of child objects, each one representing a resource assigned to the project. The collection is maintained by a ProjectResources collection object, which is cre- ated by inheriting from Csla.BusinessListBase. The ProjectResources class has three regions: • Business Methods • Factory Methods • Data Access The Business Methods region contains the Assign() method that assigns a resource to the pr oject. It also contains some helpful overloads of common methods, such as a Contains() method that accepts the Id v alue of a Resource. This is useful because the Contains() method pr o vided b y BusinessListBase() only accepts a ProjectResource object; but as you’ll see in Chapters 9 and 10, the UI code needs to see if the collection contains a ResourceInfo object based on its Id value. The Factory Methods region contains a set of internal-scoped factory methods for use by the Project object in cr eating and loading the collection with data. F inally , the D ata A ccess r egion implements code to load the collection with data, and to save the child objects in the collection into the database. B efor e getting into the regions, let’s take a look at the class declaration: [Serializable()] public class ProjectResources : BusinessListBase<ProjectResources, ProjectResource> Like all business classes, this one is serializable. It also inherits from a CSLA .NET base class— in this case, BusinessListBase. The BusinessListBase class requires two generic type parameters. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 429 6323_c08_final.qxd 2/26/06 10:02 PM Page 429 The first one is the type of the collection itself. That value is used to provide strongly typed methods such as Clone() and Save(). The second one is the type of the child objects contained within the collection. That value is used to make the collection itself strongly typed and affects many methods on the collection, including the indexer, Remove(), Contains(), and others. Business Methods The Business Methods region contains a set of methods that provide business functionality for use by UI code. In many cases, these methods are overloads of methods common to all collections, but they accept parameters that provide much simpler use for the UI developer. The methods are listed in Table 8-1. Table 8-1. Business Methods in ProjectResources Method Description Assign Assigns a resource to the project GetItem Returns a child object based on a resource Id value Remove Removes a child object based on a resource Id value Contains Searches for a child object based on a resource Id value ContainsDeleted Searches for a deleted child object based on a resource Id value Of all these methods, only Assign() is truly required. All the other methods merely provide simpler access to the collection’s functionality. Still, that simpler access translates into much less code in the UI, so it is well worth implementing in the object. Assign The Assign() method assigns a resource to the project. It accepts a resource Id value as a parame- ter, and adds a new ProjectResource object to the collection representing the assignment of the resource: public void Assign(int resourceId) { if (!Contains(resourceId)) { ProjectResource resource = ProjectResource.NewProjectResource(resourceId); this.Add(resource); } else throw new InvalidOperationException( "Resource already assigned to project"); } A resource can only be assigned to a project one time, so the collection is first checked to see if it contains an entry with that same resource Id value. Notice that already the simpler Contains() o v erload is useful—I’ ll get to its implementation shor tly . Assuming the resource isn’t already assigned, a new ProjectResource child object is created and initialized by calling the NewProjectResource() factory method. Notice that the resource Id value is passed to the new child object, establishing the pr oper connection between the project and resource. The child object is then added to the collection, completing the pr ocess . CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION430 6323_c08_final.qxd 2/26/06 10:02 PM Page 430 This means the UI code to add a resource to a project looks like this: project.Resources.Assign(resourceId); where resourceId is the primary key of the Resource to be assigned. GetItem Collections have an indexer that provides access to individual items in the collection based on a numeric index value. It is often also useful to be able to get at a specific child object based on other data in the child objects themselves. In this case, it will be necessary to retrieve a child item based on the Id property of the resource that was assigned to the project, and this requires a method that accepts the Id property and returns the corresponding child object: public ProjectResource GetItem(int resourceId) { foreach (ProjectResource res in this) if (res.ResourceId == resourceId) return res; return null; } In principle, this method operates much like an indexer—but the default indexer’s parameter is a positional index, while the GetItem() method’s parameter indicates the Id value of the resource. Simply overloading the indexer would be a cleaner solution, but this isn’t possible because the default indexer accepts an int, and so does this new “overload.” The result would be a duplicate method signature, and so this must be a method rather than an overload of the indexer. Remove, Contains, and ContainsDeleted Collections that inherit from BusinessListBase automatically have Remove(), Contains(), and ContainsDeleted() methods. Each of these accepts a reference to a child object as a parameter, and often that is sufficient. For this collection, however, it turns out that the UI code in Chapters 9 and 10 is much simpler if it is possible to remove or check for a child object based on a resource Id property value rather than a child object reference . To provide this capability, each of these three methods is overloaded with a different implementation. For instance, here’s the Remove() method: public void Remove(int resourceId) { foreach (ProjectResource res in this) { if (res.ResourceId == resourceId) { Remove(res); break; } } } This method accepts the resourceId value as a parameter, and that value is used to locate the child object (if any) in the collection. The Contains() and ContainsDeleted() overloads follow the same basic approach. N ot all collections will need o v erloads of this type , but such overloads are often useful to simplify the use of the collection and reduce code in the UI. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 431 6323_c08_final.qxd 2/26/06 10:02 PM Page 431 Factory Methods The Factory Methods region contains two factory methods and a private constructor, much like the Project class. Factory Methods T he two factory methods are declared as i nternal s cope since they are not for use by the UI code. Rather, they are intended for use by the Project object that contains the collection: internal static ProjectResources NewProjectResources() { return new ProjectResources(); } internal static ProjectResources GetProjectResources(SafeDataReader dr) { return new ProjectResources(dr); } In both cases, the factory methods simply use the new keyword to create and return a new instance of the collection object. The NewProjectResources() method returns an empty, new collection. This method is called by Project when a new Project object is cr eated. GetProjectResources() is used to load the collection with child objects based on data from the database. It is called from DataPortal_Fetch() in the Project class, when a Project object is in the process of being loaded from the database. This method accepts a data reader as a parameter, and that data reader is provided to the constructor, which is responsible for loading the collection with data. That parameterized constructor is found in the Data Access region. Constructor The default constructor, called from NewProjectResources(), is located in the Factory Methods region, just like it is in the template from Chapter 7: private ProjectResources() { MarkAsChild(); } The fact that MarkAsChild() is called here is very important. Remember that the ProjectResources collection is contained within a Project object and is a child of that Project. Due to this, the collection object must be marked as a child object as it is created. The BusinessListBase code r elies on this information to make sure the object behaves properly as a child of another object. The GetProjectResources() factory method also calls a constructor, passing it a data reader object: private ProjectResources(SafeDataReader dr) { MarkAsChild(); Fetch(dr); } This method also calls MarkAsChild(), and then calls a Fetch() method, which will actually load the object’s data from the data reader. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION432 6323_c08_final.qxd 2/26/06 10:02 PM Page 432 Data Access The Data Access region in a child collection object is quite different from that of any root object like Project. Remember that the data portal never directly interacts with child objects, leaving it instead to the root object to initiate all data access in its children. In this case, that means that the Project object is responsible for initiating all data access activity in its child ProjectResources collection. Recall that in the DataPortal_XYZ methods of Project, calls were made to the GetProjectResources() factory method and to an Update() method on the collection. Loading Data In the DataPortal_Fetch() method of Project, a call is made to the GetProjectResources() factory method in ProjectResources. That factory method calls a parameterized constructor, passing a data reader that contains the collection of data for the child objects to be loaded into the collection. That constructor then calls the following Fetch() method to load the object with data: private void Fetch(SafeDataReader dr) { RaiseListChangedEvents = false; while (dr.Read()) this.Add(ProjectResource.GetResource(dr)); RaiseListChangedEvents = true; } This method loops through all the items in the data reader, using each row of data to create a new ProjectResource child object. I’ll discuss the GetResource() factory method later in the chap- ter, but you can see that it accepts the data reader object as a parameter so the new child object can populate itself with data from the current row. As discussed in Chapter 7, the RaiseListChangedEvents property is set to false and then true to suppress the ListChanged events that would otherwise be raised as each item is added. Updating Data The DataPortal_Insert() and DataPortal_Update() methods of Project call the collection’s Update() method. This method is internal in scope, as it is intended only for use by the parent Project object. The Update() method is responsible for deleting, inserting, and updating all the child objects in the collection into the database . More precisely, it is responsible for asking each child object to do the appropriate operation. This means looping thr ough both the list of child objects marked for deletion and the list of active objects that may require insert or update operations: internal void Update(Project project) { RaiseListChangedEvents = false; // update (thus deleting) any deleted child objects foreach (ProjectResource obj in DeletedList) obj.DeleteSelf(project); // now that they are deleted, remove them from memory too DeletedList.Clear(); // add/update any current child objects foreach (ProjectResource obj in this) { if (obj.IsNew) obj.Insert(project); CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 433 6323_c08_final.qxd 2/26/06 10:02 PM Page 433 [...]... foreach (ProjectResource obj in this) { if (obj.IsNew) obj.Insert(project); else obj.Update(project); } In many ways, this approach mirrors the behavior of the data portal as implemented in Chapter 4 The state of the child object is used to determine which specific data access method to call This completes the ProjectResources collection code ProjectResource A Project contains a child collection: ProjectResources... 449 CHAPTER 8 s BUSINESS OBJECT IMPLEMENTATION private void DataPortal_Fetch(Criteria criteria) { this.RaiseListChangedEvents = false; using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "getResources"; using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader()))... GetProjectResource() factory method calls a constructor to create the object, passing a data reader object as a parameter: private ProjectResource(SafeDataReader dr) { MarkAsChild(); Fetch(dr); } This method calls MarkAsChild() to mark the object as a child object, and then calls a Fetch() method to do the actual data loading Data Access The Data Access region contains the code to initialize a new instance... Since ProjectResource is a child of ProjectResources, the constructor must call MarkAsChild(): private ProjectResource() { MarkAsChild(); } As with ProjectResources, this ensures that the object behaves properly as a child of another object When a resource is newly assigned to a project, the NewProjectResource() factory method is called It, in turn, calls a constructor to initialize the new object:... The code that runs on the server is entirely contained within the DataPortal_Execute() method: protected override void DataPortal_Execute() { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "existsProject"; cm.Parameters.AddWithValue("@id", _id); int count... ProjectResources The ProjectResources collection contains ProjectResource objects As designed in Chapter 6, each ProjectResource object represents a resource that has been assigned to the project Also remember from Chapter 6 that ProjectResource shares some behaviors with ResourceAssignment, and those common behaviors were factored out into an Assignment object As you look through the code in ProjectResource,... initialize SqlCommand objects to use the same connection and transaction The principle remains consistent, however The Update() method in ResourceAssignments accepts the open SqlConnection object and passes it to each ResourceAssignment child object’s data access method: internal void Update(SqlConnection cn, Resource resource) { this.RaiseListChangedEvents = false; // update (thus deleting) any deleted child... ProjectResources, this object has two factory methods scoped as internal These methods are intended for use only by the parent object: ProjectResources The NewProjectResource() factory method accepts a resourceId value as a parameter That value is used to retrieve the corresponding Resource object from the database: internal static ProjectResource NewProjectResource(int resourceId) { return new ProjectResource(... putting the code in this particular location ensures that it is the client-side cache that is invalidated by calling RoleList.InvalidatedCache() Invalidating the Server-Side Cache Perhaps even more subtle is the fact that there could be a cached RoleList collection on both the client and server Keep in mind that CSLA NET enables mobile objects, and that means that business object code can run on the client... locally in the client process, in which case there’s no point invalidating the cache here, since the Save() method will take care of it That’s why the code checks the ExecutionLocation to see if it’s actually running on an application server If so, it calls RoleList.InvalidateCache() to invalidate any server-side cache of role data Role The Roles object is an editable root collection that contains a list . of the child object is used to determine which specific data access method to call. This completes the ProjectResources collection code. ProjectResource A Project contains a child collection:. DataPortal_Fetch(Criteria criteria) { using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText. SqlConnection(Database.PTrackerConnection)) { cn.Open(); using (SqlCommand cm = cn.CreateCommand()) { cm.CommandType = CommandType.StoredProcedure; cm.CommandText = "deleteProject"; cm.Parameters.AddWithValue("@id",

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

TỪ KHÓA LIÊN QUAN