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

Expert VB 2005 Business Objects Second Edition phần 8 potx

69 268 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 69
Dung lượng 1,54 MB

Nội dung

Dim principal As New PTPrincipal(identity) Csla.ApplicationContext.User = principal End If Return identity.IsAuthenticated End Function Notice that PTIdentity has a factory method; in fact, it is derived from Csla.ReadOnlyBase and so is a full-fledged business object. The username and password parameters are passed to the PTIdentity object’s factory method. Of course, the factory method calls the data portal, which ulti- m ately invokes the D ataPortal_Fetch() m ethod in P TIdentity . As you’ll see, that method validates the credentials against the database. With a PTIdentity object created, its IsAuthenticated property can be checked to see if the user’s credentials were valid. If they were valid, the identity object is used to create a new PTPrincipal object, and that object is set to be the current principal by using the ApplicationContext object’s User property, as discussed in Chapter 4: Dim principal As New PTPrincipal(identity) Csla.ApplicationContext.User = principal If the credentials weren’t valid, then the current principal value is left unchanged. In any case, the IsAuthenticated value is returned as a r esult so that the UI code can take appropriate steps based on whether the user was successfully logged in or not. Logout The Logout() method is much simpler . All it needs to do is ensur e that the current principal value is set to an unauthenticated principal object—that means a principal object whose identity object has an IsAuthenticated property which returns False: Public Shared Sub Logout() Dim identity As PTIdentity = PTIdentity.UnauthenticatedIdentity Dim principal As New PTPrincipal(identity) Csla.ApplicationContext.User = principal End Sub To achiev e this result, an unauthenticated PTIdentity object is created b y calling a special fac- tory method for that purpose. That identity object is then used to create a new PTPrincipal object, and it is set as the current principal by setting ApplicationContext.User. The reason for creating an unauthenticated PTPrincipal rather than an unauthenticated GenericPrincipal (a built-in .NET type) is to support anonymous or guest users. Recall from Chapter 4 that the data por tal will only accept principal objects that subclass BusinessPrincipalBase when custom authentication is used. This means the data portal will throw an exception if a GenericPrincipal is passed to the application server. So if the application is to support anonymous (i.e., unauthenticated) users, then the principal must be an unauthen- ticated PTPrincipal, as shown here. PTIdentity As you’ve seen, PTPrincipal isn’t overly complex. It leaves most of the work to PTIdentity, including implementing the IsInRole() functionality and v erification of the user’s credentials. PTIdentity is a read-only object, and so it inherits from Csla.ReadOnlyBase. It is also a .NET identity object, so it must implement System.Security.Principal.IIdentity: CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION460 6315_c08_final.qxd 4/7/06 2:00 PM Page 460 <Serializable()> _ Public Class PTIdentity Inherits ReadOnlyBase(Of PTIdentity) Implements IIdentity Being a read-only root object, PTIdentity follows the appropriate template from Chapter 7, including Business Methods, Factory Methods, and Data Access regions. It doesn’t implement an Authorization Rules region because it has no authorization rules. Business Methods Because PTIdentity implements the IIdentity interface, it is required to implement the AuthenticationType, IsAuthenticated, and Name properties: Private mIsAuthenticated As Boolean Private mName As String = "" Public ReadOnly Property AuthenticationType() As String _ Implements System.Security.Principal.IIdentity.AuthenticationType Get Return "Csla" End Get End Property Public ReadOnly Property IsAuthenticated() As Boolean _ Implements System.Security.Principal.IIdentity.IsAuthenticated Get Return mIsAuthenticated End Get End Property Public ReadOnly Property Name() As String _ Implements System.Security.Principal.IIdentity.Name Get Return mName End Get End Property These are all read-only properties and are quite straightforward. Also, because it is a subclass of ReadOnlyBase, the class must implement the GetIdValue() method: Protected Overrides Function GetIdValue() As Object Return mName End Function Finally, the code in PTPrincipal requires that PTIdentity implement an IsInRole() method to determine whether the user is in a specified role: Private mRoles As New List(Of String) Friend Function IsInRole(ByVal role As String) As Boolean Return mRoles.Contains(role) End Function This method is Friend in scope because it is only intended for use by PTPrincipal. All it does is determine whether the specified rule exists in the list of roles for the user. That list is populated in DataPortal_Fetch(), assuming the user ’ s credentials are valid. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 461 6315_c08_final.qxd 4/7/06 2:00 PM Page 461 Factory Methods Like all read-only root objects, PTIdentity implements a factory method so it can be created. In fact, it implements two factory methods: one to verify a set of credentials, and one to return an unauthenticated identity object to support the concept of anonymous users. The UnauthenticatedIdentity() factory method is simple: Friend Shared Function UnauthenticatedIdentity() As PTIdentity Return New PTIdentity E nd Function Because mIsAuthenticated defaults to False, mName defaults to an empty value, and mRoles defaults to being an empty list, simply creating an instance of the object is enough to provide an unauthenticated identity object with no username and no roles. The GetIdentity() factory, on the other hand, creates a Criteria object and calls the data portal so that the DataPortal_Fetch() method can verify the supplied username and password parameter values: Friend Shared Function GetIdentity( _ ByVal username As String, ByVal password As String) As PTIdentity Return DataPortal.Fetch(Of PTIdentity)(New Criteria(username, password)) End Function This is a standard factory method to retrieve an object populated from the database. Data Access The DataPortal_Fetch() method actually performs the authentication: verifying the user’s creden- tials against the values in the database. In a real application, you should store passwords as hashed or encrypted values; but for a sample application, it is simpler to store them as clear text. The Criteria object passed fr om the GetIdentity() factor y method to DataPortal_Fetch() is the most complex in the application: <Serializable()> _ Private Class Criteria Private mUsername As String Private mPassword As String Public ReadOnly Property Username() As String Get Return mUsername End Get End Property Public ReadOnly Property Password() As String Get Return mPassword End Get End Property Public Sub New(ByVal username As String, ByVal password As String) mUsername = username mPassword = password End Sub End Class CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION462 6315_c08_final.qxd 4/7/06 2:00 PM Page 462 Of course, “complex” is a relative term. Obviously, there’s nothing overly complex about a class that exposes two read-only properties. But this illustrates how the Criteria object concept can be used to pass complex criteria to the DataPortal_XYZ methods as needed. The DataPortal_Fetch() method itself accepts this Criteria object and calls the Login stored procedure created in Chapter 6: Private Overloads Sub DataPortal_Fetch(ByVal criteria As Criteria) Using cn As New SqlConnection(Database.SecurityConnection) cn.Open() Using cm As SqlCommand = cn.CreateCommand cm.CommandText = "Login" cm.CommandType = CommandType.StoredProcedure cm.Parameters.AddWithValue("@user", criteria.Username) cm.Parameters.AddWithValue("@pw", criteria.Password) Using dr As SqlDataReader = cm.ExecuteReader() If dr.Read() Then mName = criteria.Username mIsAuthenticated = True If dr.NextResult Then While dr.Read mRoles.Add(dr.GetString(0)) End While End If Else mName = "" mIsAuthenticated = False End If End Using End Using End Using End Sub The method uses standard ADO.NET data access code. It opens a connection to the database (calling a Database.SecurityConnection helper to get the connection string for the security data- base). Then it sets up a SqlCommand object, loading it with the Username and Password properties from the Criteria object. When the command is executed, the resulting data reader object will either contain data or it won’t—if it contains data, then the user’s credentials were valid, otherwise they were invalid. Given v alid cr edentials , the object’s fields are loaded with data from the database, and the list of roles are loaded into the mRoles collection: mName = criteria.Username mIsAuthenticated = True If dr.NextResult Then While dr.Read mRoles.Add(dr.GetString(0)) End While End If On the other hand, if the credentials were not valid, the object’s fields are set to appropriate values for an unauthenticated identity: mName = "" mIsAuthenticated = False mRoles.Clear() CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 463 6315_c08_final.qxd 4/7/06 2:00 PM Page 463 The end result is a populated PTIdentity object: either authenticated or unauthenticated. Either way, the object is returned to the client where it can be used to create a PTPrincipal object to support authorization activities within the business objects and the UI. Conclusion This chapter implemented the business objects designed in Chapter 6, using the templates and c oncepts discussed in Chapter 7. The result is P rojectTracker.Library , the business layer for the sample ProjectTracker application, including the following: • Project • ProjectResources • ProjectResource • Resource • ResourceAssignments • ResourceAssignment • Assignment • RoleList • Roles • Role The library also includes classes to support custom authentication: • PTPrincipal • PTIdentity This business library will be used to create Windows Forms, Web Forms, and Web Services interfaces in the next three chapters. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION464 6315_c08_final.qxd 4/7/06 2:00 PM Page 464 Windows Forms UI Up to this point, the focus has been on the business layer of the application. Chapters 6 through 8 walked through the design and creation of business objects and logic. Now let’s shift gears and look at how a user interface can be created based on those business objects. This chapter will describe a Windows Forms interface. Windows Forms is a flexible technology that can be used to create a great many types of user interfaces, as evidenced by the fact that there are entire books on Windows Forms UI development. I won’t rehash that sort of material in this book; what I want to focus on here is how to make effec- tive use of business objects and collections to create Windows Forms displays and entry forms. When creating the CSLA .NET framework, quite a bit of effort was spent to allow business objects to support Windows Forms development. The business objects themselves are focused on modeling the business behaviors described in the use cases from Chapter 6. At the same time, the fact that they inherit from CSLA .NET base classes means they possess quite a few important features that are very useful for creating a Windows Forms UI. Most important is the support for Windows Forms data binding. Although you could certainly write your own code to move the data between properties of business objects and the controls on a form, it’s far easier to use data binding whenev er possible. The user interface is centered around user controls. Each form will be created as a user control, rather than a Form object. That way, each form can be dynamically loaded into many styles of inter- face, including the multiple document interface (MDI), multipane user interfaces such as Microsoft Outlook, the single document interface (SDI), and other styles. The style in this chapter uses a sin- gle Form object that hosts the controls, showing just one at a time. This provides the user with a simple , easily understandable inter face. The impor tant thing is that the chapter will illustrate the use of user controls, and how to dynamically host them. You can easily adapt this code to implement a wide variety of different UI styles. But above all, my focus in this chapter is to show how easy it is to create an interface, given that the business objects alr eady implement all the business logic, including v alidation, manipulation, author ization, and data access . The result is that there’s only minimal code in the UI, and that code is focused only on user interaction. The chapter starts by laying out the basic design of the interface, and then walks through the common behaviors of the menu, status display, and authentication. Once that’s done, I’ll discuss the creation of forms to view and edit data using the DataGridView and detail controls. I’ll also show how to create and use dialog forms. Interface Design The UI application can be found within the ProjectTracker solution. The project is named PTWin. The design of the PTWin inter face is that of a single main form with a menu and status bar. This 465 CHAPTER 9 ■ ■ ■ 6315_c09_final.qxd 4/7/06 2:12 PM Page 465 main form dynamically loads user controls and displays them to the user. Figure 9-1 shows what the main form looks like. Notice that the menu bar includes menus that deal with projects, resources, roles, and authen- tication. When the user chooses a menu option, a user control is dynamically loaded into the main area of the form. Figure 9-2 shows the application while the user is editing a project. 466 CHAPTER 9 ■ WINDOWS FORMS UI Figure 9-1. Appearance of the main form Figure 9-2. E diting a pr oject 6315_c09_final.qxd 4/7/06 2:12 PM Page 466 Of course, there are some dialog windows used to collect input from the user as well, but the bulk of the application’s functionality centers around the use of user controls hosted by the main form. Table 9-1 lists the forms and controls that make up the interface. Table 9-1. Forms and User Controls in PTWin Form/Control Type Description MainForm Form The main form for the application LoginForm Form A login dialog to collect user credentials RolesEdit Control Allows the user to edit the list of roles ProjectSelect Form A dialog prompting the user to select from a list of projects ProjectEdit Control Allows the user to view, add, or edit a project ResourceSelect Form A dialog prompting the user to select from a list of resources ResourceEdit Control Allows the user to view, add, or edit a resource It is very important that you understand that all the data binding and business functionality covered in this chapter works exactly the same with regular forms as it does with user controls. I am using user controls in this chapter because I think it is a best practice for Windows Forms UI design, but this has no impact on the way data binding is used to create the UI against the business objects created in Chapter 8. The user control approach taken in this chapter gives you a great deal of flexibility. You can host the user controls, as shown in this chapter, you can host them in child forms in an MDI inter- face , or y ou can host them in panes in a multipane interface. In short, by creating your “forms” as user controls, you gain the flexibility to use them in many different types of UI design. User Control Framework Dynamically loading a user control isn’t difficult. The code needs to follow this basic process: 1. Create the control. 2. Add the control to the form’s Controls collection. 3. Set the control’s properties for size/position. 4. M ake the contr ol visible ( Visible = True). 5. S et the contr ol ’s z-order ( BringToFront()). This is simple enough—ho w ev er , integr ating the user contr ols into the main form display nicely requires some extra work. In particular, the UI in this chapter supports the following: • A Documents menu • Notification when the user logs in or out • Bringing an existing control forward when appropriate • Centralized status text and cursor handling Let’s quickly discuss what I mean by each of these bullet points. If you look at Figure 9-1, you’ll notice that there’s a Documents item on the menu bar, but it’s disabled. In Figure 9-2, it’s enabled. This is because ther e’s now a document (user control) loaded in the application. In fact, multiple documents can be loaded at the same time , and this Documents menu allo ws the user to switch between them. CHAPTER 9 ■ WINDOWS FORMS UI 467 6315_c09_final.qxd 4/7/06 2:12 PM Page 467 ■Note This application uses a Documents menu rather than a Windows menu because the menu allows the user to switch between various documents, not between windows. If you were creating a user interface in which t he user chooses to display or arrange different windows, you would name the menu “Windows.” Both figures also show that the user is logged in with the name rocky, and that there’s a Logout button available on the menu bar. Look back at Figure 9-2 and notice how the user is allowed to edit the fields in the form. Now look at Figure 9-3, in which the user is not allowed to edit any of the fields. The reason for this is that the user isn’t logged in. This is clearly shown in the menu bar, which now has a Login button instead of a Logout button. To make this authorization behavior work, the main form must be able to notify all the loaded user controls when the current user logs in or out. That way, each user control can enable and disable its contr ols based on the authorization properties of the business object being edited by the form. The hard work is actually handled by the ReadWriteAuthorization control created in Chapter 5. Still, each user control must be notified about the fact that the user logged in or out so that the authorization code can be triggered. I f the user has a number of documents open in the application, he can only see the one in front—the active document. He could easily try to open the same document a second time, and this should result in the already open document being brought to the front to be the new active document. For instance, suppose the user opens project A. Then he opens some other projects and resources, so project A is no longer active. Then suppose the user again tries to open project A. In that case, the application won’t open a new document—rather, it will find the already open document for project A and will make it the active document. Finally, as the user interacts with a document, many things may happen, some of which can take a while . The user may load or save data, start a complex computing task, or any number of CHAPTER 9 ■ WINDOWS FORMS UI468 Figure 9-3. Vie wing a project 6315_c09_final.qxd 4/7/06 2:12 PM Page 468 things that may take some time. When this happens, the main form’s status bar should show text telling the user what is going on, and the mouse cursor should change to indicate that the appli- cation is busy. It is not good to write code in every user control to handle the details of the Documents menu. This code must detect login/logout activity, avoid duplicate documents, and display status to the user. That is all plumbing code that should be written once and reused by user controls. Although my intent with this chapter isn’t to create a full-blown Windows Forms UI framework, t hese issues must be addressed for a basically decent user experience. User Control Design The user will primarily interact with user controls hosted within the main form. In Visual Studio, each user control is really just like a regular form. Visual Studio even provides a user control designer surface , which y ou can use to create the user control just like you would normally create a form. In order to support the features discussed in the previous section, each user control needs some common functionality. To provide this functionality with the minimum amount of manual coding, the PTWin project includes a WinPart control. Each user control inherits from WinPart, rather than directly from UserControl. The WinPart base control implements behaviors common to all user controls that are to be hosted in the main form, including the following: • Overrides for common System.Object methods • Event notification for the pr ocess of closing • Event notification when the current user’s principal object is changed By inheriting from WinPart, a user control can often include no extra code beyond a simple GetIdValue() method, which must be implemented to return a unique identifier for the instance of the user control. In most cases, this method simply returns the business object being edited by the for m. All other code in a typical user control centers around user interaction—dealing with button clicks, text changes, and so forth. Application Configuration The application needs to provide some basic configuration information through the application’s configuration file. I n the client application configur ation file, you can either provide connection strings so that the application can inter act with the database dir ectly , or y ou can configur e the data por tal to com- municate with a remote application server. The basic concept here was discussed in Chapter 4 when the channel adapter implementation was covered. Recall that the data portal supports three possible channels: r emoting, E nterprise Services, and Web Services. You can create your own chan- nels as well if none of these meet your needs. In Chapter 1, I discussed the trade-offs between performance, scalability, fault tolerance, and security that come with various physical n-tier configurations. The most scalable solution for an intelligent client UI is to use an application server to host the data access layer, while the most performant solution is to run the data portal locally in the client process. In this chapter, I’ll show first ho w to r un the data por tal locally , and then r emotely using each available channel. Chapter 12 will demonstrate how to create the three types of remote data portal hosts for use by the PTWin application. The configur ation is controlled by the application’s configuration file. In the Visual Studio proj- ect, this is named App.config. CHAPTER 9 ■ WINDOWS FORMS UI 469 6315_c09_final.qxd 4/7/06 2:12 PM Page 469 [...]... data binding in Windows Forms uses BindingSource controls These controls sit between all the data-bound controls in the UI and the actual data source object—in this case, Roles 487 6315_c09_final.qxd 488 4/7/06 2:12 PM Page 488 CHAPTER 9 s WINDOWS FORMS UI Figure 9-9 RolesEdit user control with data-bound DataGridView The first thing you might notice about Figure 9-9 is the ToolStrip control across the... editable data The next line of code creates a clone of the business object: Dim temp As Admin.Roles = mRoles.Clone This is easily done, since all CSLA NET business objects automatically support the Clone() method Remember that this method copies the object and all child objects it contains In this case, it copies the Roles object and all the Role objects in the collection Then the copy of the object is... collection object such as ProjectTracker.Library.Roles Using a Business Class As a Data Source To bind controls to an object, choose the Data ® Add New Data Source menu option in Visual Studio to bring up the Data Source Configuration Wizard Choose the Object option in the first step, as shown in Figure 9-6 485 6315_c09_final.qxd 486 4/7/06 2:12 PM Page 486 CHAPTER 9 s WINDOWS FORMS UI Figure 9-6 Choosing an... Data ® Show Data Sources menu item in Visual Studio Figure 9 -8 shows the Data Sources window after all the root classes from Chapter 8 have been added as data sources Figure 9 -8 ProjectTracker.Library classes in the Data Sources window Notice how the classes are grouped by namespace to help you find them more easily The illustration in Figure 9 -8 shows the Roles class expanded to show its properties When... Similarly, when the object is created, it sets the mouse cursor to a busy cursor, and resets it when disposed 477 6315_c09_final.qxd 4 78 4/7/06 2:12 PM Page 4 78 CHAPTER 9 s WINDOWS FORMS UI To do this, it needs to be able to access the MainForm object Fortunately VB 2005 supports default instances for forms If your application has exactly one instance of a specific form, such as MainForm, then you can... DataPortal_Fetch() method of the Roles object, a Csla.DataPortalException will be thrown To get at the original exception thrown by the business code, use the BusinessException property Remember that you can also use the BusinessObject property to get a reference to the business object as it was when the exception was thrown—a fact that can be very useful for debugging It is far less likely that any other... existing winpart 483 6315_c09_final.qxd 484 4/7/06 2:12 PM Page 484 CHAPTER 9 s WINDOWS FORMS UI ShowWinPart(part) Exit Sub End If End If Next ' the resource wasn't already loaded ' so load it and display the new winpart Using busy As New StatusBusy("Loading resource ") Try AddWinPart(New ResourceEdit(Resource.GetResource(resourceId))) Catch ex As Csla.DataPortalException MessageBox.Show(ex.BusinessException.ToString,... Choosing an object data source The next step in the wizard is to select the business class that will be the data source All types in the current project and any referenced projects are listed As shown in Figure 9-7, they are grouped by namespace Figure 9-7 Selecting the data source business class 6315_c09_final.qxd 4/7/06 2:12 PM Page 487 CHAPTER 9 s WINDOWS FORMS UI s Tip This wizard uses reflection to get... Notice how the actual authorization check is delegated to the Shared method of the Project business class These methods were discussed in Chapter 8, and were implemented specifically to enable scenarios like this The idea is that MainForm has no idea whether particular users or roles are authorized to add Project objects Instead, the Project class itself has that knowledge, and MainForm simply asks Project... System.Security.Principal.PrincipalPolicy.WindowsPrincipal) Else DoLogin() End If If DocumentCount = 0 Then Me.DocumentsToolStripDropDownButton.Enabled = False End If ApplyAuthorizationRules() End Sub 481 6315_c09_final.qxd 482 4/7/06 2:12 PM Page 482 CHAPTER 9 s WINDOWS FORMS UI Calling SetPrincipalPolicy() to set the WindowsPrincipal option tells the NET runtime to return the current WindowsPrincipal object for the CurrentPrincipal . user ’ s credentials are valid. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION 461 6315_c 08_ final.qxd 4/7/06 2:00 PM Page 461 Factory Methods Like all read-only root objects, PTIdentity implements a factory. PTPrincipal object to support authorization activities within the business objects and the UI. Conclusion This chapter implemented the business objects designed in Chapter 6, using the templates and c oncepts. PTIdentity This business library will be used to create Windows Forms, Web Forms, and Web Services interfaces in the next three chapters. CHAPTER 8 ■ BUSINESS OBJECT IMPLEMENTATION464 6315_c 08_ final.qxd

Ngày đăng: 12/08/2014, 16:21