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

Apress Expert C sharp 2005 (Phần 12) docx

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

Tiêu đề Web Forms UI Configuration
Trường học University of CreateYourSchool (https://www.createyouruniversity.edu)
Chuyên ngành Computer Science
Thể loại Lecture notes
Năm xuất bản 2006
Thành phố Unknown
Định dạng
Số trang 50
Dung lượng 0,96 MB

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

Nội dung

The most important thing to realize about the site configuration is that the data portal can bechanged from local to remote using any of the network channels with no need to change any U

Trang 1

If you want to use Windows authentication, change the configuration to this:

<add key="CslaAuthentication" value="Windows" />

Of course, that change would require coding changes To start, the PTPrincipal and PTIdentityclasses should be removed from ProjectTracker.Library, as they would no longer be needed Also,the login/logout functionality implemented in this chapter would become unnecessary Specifically,the Login form and the code to display that form would be removed from the UI project

Local Data Portal

The web.config file also controls how the application uses the data portal To have the website interactdirectly with the database, use the following (with your connection string changed to the connectionstring for your database):

Tip In the code download for this book, the PTrackerand Securitydatabase files are in the solution directory,not in the website’sApp_Datadirectory This means that you can’t use a local data portal from the website withoutfirst copying the database files into the App_Datadirectory and changing the connection strings accordingly

Remote Data Portal (with Remoting)

To have the data portal use an application server and communicate using the remoting channel, the configuration would look like this:

Trang 2

Before using this configuration, the remoting host virtual root must be created and configured.

I’ll show how this is done in Chapter 12

Remote Data Portal (with Enterprise Services)

Similarly, to use the Enterprise Services channel the configuration would look like this:

station The basic steps were discussed in Chapter 4, and I’ll show how this is done in Chapter 12

Remote Data Portal (with Web Services)

Finally, to use Web Services, the configuration would look like this:

be created and configured I’ll show how this is done in Chapter 12

The most important thing to realize about the site configuration is that the data portal can bechanged from local to remote (using any of the network channels) with no need to change any UI

or business object code

PTWeb Site Setup

The UI application can be found within the ProjectTracker solution The project is named PTWeb

The site references the ProjectTracker.Library project, as shown in Figure 10-8 This causesVisual Studio to automatically put the associated Csla.dll files into the Bin directory as well, because

Csla.dll is referenced by ProjectTracker.Library

Trang 3

Hosting in IIS

The PTWeb website will only run within IIS, not within the ASP.NET Development Server (commonlyknown as Cassini or VS Host) The reason for this is explained later in the chapter in the “Forms-Based Authentication” section

To host a website in IIS during development, you need to take the following steps:

1. Set up a virtual root in IIS that points to the directory containing the PTWeb project files

2. Set the virtual root to use ASP.NET 2.0, using the ASP.NET tab of the virtual root propertiesdialog in the IIS management console

3. Set the website’s start options using the project properties dialog in Visual Studio 2005.Change the setting to use a custom server so it starts up using IIS with a URL such ashttp://localhost/PTWeb

With the basic website setup complete, let’s go through the creation of the Web Forms UI First,I’ll discuss the use of a master page, and then I’ll cover the process of logging a user in and out usingforms-based authentication

With the common code out of the way, I’ll discuss the process of maintaining the roles andproject data in detail At that point, you should have a good understanding of how to create bothgrid-based and detail pages

Master Page

To ensure that all pages in the site have the same basic layout, navigation, and authentication

options, a master page is used The master page provides these consistent elements, and all the rest

of the pages in the site are content pages This means they fit within the context of the master page

itself, adding content where appropriate

Figure 10-8.Referencing ProjectTracker.Library

Trang 4

Look back at Figures 10-6 and 10-7 to see the visual appearance of the pages Both Default.aspxand ProjectEdit.aspx are content pages, adding their content to that already provided by

This is the area where content pages provide their content, and it is the main body of the page

You’ll see how each content page provides content for this area later in the chapter

Trang 5

Theme Support

ASP.NET 2.0 supports the concept of themes for a website, where the visual appearance of the site is

defined by a theme: a group of files in a theme-specific subdirectory beneath the App_Themes

direc-tory in the virtual root A theme is a group of style sheets, graphics, and control skins that describethe appearance of a site A given site can have many themes, and you can even allow the user tochoose between them if you so desire

Notice how all of the regions in the master page are set up using div tags No appearance acteristics are specified in the page itself Instead, the actual appearance is defined by a CSS stylesheet contained within the current theme for the site The PTWeb site includes and uses a Basic theme.The use of the Basic theme is set up in web.config:

char-<pages theme="Basic" styleSheetTheme="Basic">

The theme property sets the default runtime theme, while styleSheetTheme sets the theme for use

at design time in Visual Studio The styleSheetTheme property should be removed when the website isdeployed to a production server

The files defining this theme are in the App_Themes/Basic folder beneath the virtual root Youshould notice that the names of the css and skin files match the name of the theme folder itself.Having the names match allows ASP.NET to automatically realize that it needs to use these files whenthe theme is selected for the website The files in this theme are listed in Table 10-4

Table 10-4.Files in the Basic Theme

File Description

Basic.css The style sheet for the site

Basic.skin The skins for GridView, DetailsView, and Login controls

Images\background.jpg The background graphic for the header region

Images\corner.png The graphic for the rounded corner in the upper-left

Combined, these files define the look and feel of the site This includes defining the appearance

of the regions in MasterPage.master For instance, the header region is defined in the css file like this:

Trang 6

<asp:Login runat="server" BackColor="#DEDEDE" BorderColor="Black"

BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana"

Each type of control in Web Forms has different options you can set in a skin file, allowing you

to set the appearance of each control in many ways

By making the site theme-enabled, you can easily change the appearance of the site later bycreating a new theme directory and similar theme files, and setting the theme property in web.config

to use the new theme

Header Region

The header region of the page is the title area across the top It contains a single Label control named

PageTitle This control displays the title of the current content page, based on the Title property set

for that page The following code is included in MasterPage.master to load this value:

protected void Page_Load(object sender, EventArgs e)

The navigation region displays the navigation links down the left side of each page To do this,

a web.sitemap file and associated SiteMapDataSource control are used to load the overall structure

of the site into memory This data is then data bound to a TreeView control for display to the user

The web.sitemap file is an XML file that contains a node for each page to be displayed in thenavigation region:

<?xml version="1.0" encoding="utf-8" ?>

<siteMap

xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

<siteMapNode url="" title="" description="">

<siteMapNode url="~/Default.aspx" title="Home"

except the first one That root node is required in the file, but since I’m defining a flat structure, it

really doesn’t represent a page and is just a placeholder If you were to define a hierarchical page

structure, that node would typically point to Default.aspx

Trang 7

Notice that MasterPage.master includes a SiteMapDataSource control:

<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server"

ShowStartingNode="False" />

This special data control automatically reads the data from the web.sitemap file and makes itavailable to controls on the page The ShowStartingNode property is set to False, indicating that theroot node in web.sitemap is to be ignored That’s perfect, because that node is empty and shouldn’t

be displayed

In this case, a TreeView control in the navigation region is bound to the SiteMapDataSource,

so it displays the items listed in web.sitemap to the user

LoginStatus Control

In the subnavigation region of MasterPage.master, you’ll see a LoginStatus control:

<asp:LoginStatus ID="LoginStatus1"

runat="server" OnLoggingOut="LoginStatus1_LoggingOut" />

This is one of the login controls provided with ASP.NET 2.0, and its purpose is to allow the user

to log into and out of the site The control automatically displays the word Login if the user is loggedout, and Logout if the user is logged in When clicked, it also automatically redirects the user to alogin web page defined in web.config I’ll cover the web.config options later

Because the control automatically directs the user to the appropriate login page to be logged

in, no code is required for that process However, code is required to handle the case in which theuser clicks the control to be logged out This code goes in the master page:

protected void LoginStatus1_LoggingOut(

object sender, LoginCancelEventArgs e) {

ProjectTracker.Library.Security.PTPrincipal.Logout();

Session["CslaPrincipal"] = Csla.ApplicationContext.User;

System.Web.Security.FormsAuthentication.SignOut();

}

This code covers a lot of ground

First, the Logout() method of PTPrincipal is called, which sets the current principal on thecurrent Thread object to an unauthenticated PTPrincipal object This was discussed in Chapter 8and used in PTWin in Chapter 9

However, when the user is logged in, their principal object is stored in a Session field so it can

be easily reloaded on every page request The details on how this works are discussed later in thechapter When the user logs out, that Session field is updated to reference the new principal object

Note If you want to avoid Session, you can choose to reload the user’s identity and roles from the securitydatabase on every page request While that avoids the use of Session, it can put a substantial workload on yoursecurity database server In PTWeb, I have opted to use Sessionto minimize the load on the database

The final step is to tell ASP.NET itself that the user is no longer authenticated This is done by ing FormsAuthentication.SignOut() This method invalidates the security cookie used by ASP.NET toindicate that the user has been authenticated The result is that ASP.NET sees the user as unauthenti-cated on all subsequent page requests

call-This covers the logout process, but the login process requires some more work While the Login➥Status control handles the details of directing the user to a login page, that page must be created

Trang 8

Login Page

Like the PTWin smart client, the PTWeb site is designed to use custom authentication, so I can

illus-trate the custom authentication support provided by CSLA NET I’ll also briefly discuss the use of

Windows integrated security and the ASP.NET membership service

In Web Forms, when using custom authentication, you need to configure the site appropriatelyusing web.config, and implement a login web page to collect and validate the user’s credentials

That’s the purpose behind Login.aspx

Forms-Based Authentication

When using forms-based authentication, users are often automatically redirected to a login form

before being allowed to access any other pages Alternately, anonymous users can be allowed to use

the site, and they can choose to log into the site to gain access to extra features or functionality The

specific behaviors are defined by web.config

Before moving on, remember that the following implementation only works within IIS TheASP.NET Development Server provided with Visual Studio has various limitations; among them is

the inability to load custom security objects from assemblies in the Bin directory This means you

can’t use the ASP.NET Development Server to test or debug custom principal objects, custom

mem-bership providers, or other custom security objects if they’re in an assembly referenced from the

project

Though this is an unfortunate limitation, it can be argued that the ASP.NET Development Server

is not intended for anything beyond hobbyist or casual usage, and that IIS should be used for any

seri-ous business development

Note An alternative solution is to install the assembly containing your custom principal and identity classes into

the NET Global Assembly Cache (GAC) For PTWeb, this would mean giving ProjectTracker.Librarya strong

name and using the gacutil.execommand line utility to install the assembly into the GAC.ProjectTracker

Librarywould need to be updated in the GAC after each time you build the assembly I find that using IIS is a far

simpler solution than using the GAC

Configuring the Site

Using forms-based security in ASP.NET means that web.config includes elements like this:

unauthenti-■ Note To require users to log in before seeing any pages, replace <allow users="*"/>with <deny users="?"/>

It is important that you also ensure that the security on the virtual root itself (within IIS) is figured to allow anonymous users If IIS blocks anonymous users, then it doesn’t really matter what

con-kind of security you use within ASP.NET

Trang 9

Note Remember that IIS security runs first, and then any ASP.NET security is applied.

With the web.config options shown previously, users can use the site without logging in, but theconcept of logging in is supported The goal is the same as with PTWin in Chapter 9: allow all users to

do certain actions, and allow authenticated users to do other actions based on their roles

When a user chooses to log in, the <forms> tag specifies that they will be directed to Login.aspx,which will collect and validate their credentials Figure 10-9 shows the appearance of Login.aspx

Now this is where things get kind of cool There is no code behind Login.aspx This page uses

the ASP.NET Login control:

<asp:Login ID="Login1" runat="server">

automati-You can write code to handle the events of the Login control if you desire, but a membership

provider offers a cleaner solution overall Of course, the membership provider that comes with

Figure 10-9.Layout of the Login page

Trang 10

ASP.NET doesn’t understand PTPrincipal and PTIdentity objects, so PTWeb includes its own custom

membership provider

Custom Membership Provider

A membership provider is an object that inherits from System.Web.Security.MembershipProvider

to handle all aspects of membership These aspects include:

• Validating user credentials

• Adding a new user

should create your own security library with appropriate objects

But PTPrincipal does understand how to validate a user’s credentials Fortunately, it is possible to

implement a subset of the complete membership provider functionality, and that’s what I do in PTWeb

The PTMembershipProvider class is in the App_Code directory, so ASP.NET automatically compiles

it and makes it available to the website This class inherits from MembershipProvider and overrides

the ValidateUser() method:

public class PTMembershipProvider : MembershipProvider

{

public override bool ValidateUser(

string username, string password) {

bool result = PTPrincipal.Login(username, password);

HttpContext.Current.Session["CslaPrincipal"] = Csla.ApplicationContext.User;

imple-Notice how the ValidateUser() method already accepts username and password parameters

This is convenient because the Login() method of PTPrincipal accepts those parameters as well

The code simply calls the Login() method and records the result; true if the user was logged in,

false otherwise

Remember from Chapter 8 that the Login() method sets the User property ofCsla.ApplicationContext, thus automatically setting either the Thread object’s

CurrentPrincipal property or the HttpContext.Current.User property to an authenticated

PTPrincipal if the user’s credentials were valid; otherwise, it is set to an unauthenticated

PTPrincipal Since this code will be running within ASP.NET, it is the HttpContext value that is set

to the user’s principal

The code then sets a Session field, CslaPrincipal, to contain this principal value so it will

be available to subsequent pages

Then the result value is returned The ASP.NET membership infrastructure relies on thisreturn value to know whether the user’s credentials were valid or not

Before this custom membership provider can be used, it must be defined in web.config

as follows:

Trang 11

auto-Reloading the Principal

At this point, you’ve seen how the user can log in or out using the LoginStatus control on themaster page And you’ve seen how Login.aspx and the custom membership provider are used

to gather and validate the user’s credentials

But how does the principal object carry forward from page to page? Remember that the webtechnologies are stateless by default, and it is up to the web developer to manually implement statemanagement as she chooses Unfortunately, this extends to the user’s identity as well

The forms-based security infrastructure provided by ASP.NET writes an encrypted cookie to the user’s browser That cookie contains a security ticket with a unique identifier for the user, theuser’s name, and an expiration time This cookie flows from the browser to the web server on eachpage request, so that basic information is available

Notice, however, that the cookie doesn’t include the principal and identity objects That isbecause those objects could be quite large, and in some cases, might not even be serializable.Though PTPrincipal and PTIdentity are serializable, they could still be large enough to pose aproblem if you tried to write them to the cookie Cookies have a size limit, and remember thatPTIdentity contains an array with all the role names for the user Given a large number of roles

or lengthy role names, this could easily add up to a lot of bytes of data

Note It is possible to serialize the principal and identity objects into the cookie (if the objects are serializable).Doing so isn’t recommended, however, due to the size limitations on cookies

It is quite possible to reload PTPrincipal and PTIdentity from the security database on everypage request Remember that the ASP.NET security cookie contains the username value, and youalready know that the user was authenticated All you would need is another stored procedure inthe database that returns the user information based on username alone; no password would beprovided or checked Similarly, another static method like Login() would be implemented inPTPrincipal to load the objects based only on the username value

There are two drawbacks to this First, reloading this data from the security database on everypage request could cause a serious performance issue The security database could get overloadedwith all the requests Second, there’s an obvious security risk in implementing methods that allowloading user identities without having to supply the password While that functionality wouldn’t

Trang 12

be exposed to the end user, it makes it easier for accidental bugs or malicious back-door code to

creep into your website

This is why I use Session to store the principal object in PTWeb The user’s credentials are dated, and the resulting principal object is placed in a Session field named CslaPrincipal On all

vali-subsequent page requests, this value is retrieved from Session and is used to set both the current

Thread and HttpContext object’s principals

The work occurs in Global.asax, as this file contains the event handlers for all events leading

up to a page being processed In this case, it is the AcquireRequestState event that is used:

protected void Application_AcquireRequestState(

object sender, EventArgs e) {

System.Security.Principal.IPrincipal principal;

try { principal = (System.Security.Principal.IPrincipal) HttpContext.Current.Session["CslaPrincipal"];

} catch { principal = null;

}

if (principal == null) {

// didn't get a principal from Session, so // set it to an unauthenticted PTPrincipal ProjectTracker.Library.Security.PTPrincipal.Logout();

} else { // use the principal from Session Csla.ApplicationContext.User = principal;

} }

The reason for using the AcquireRequestState event, rather than the more obviousAuthenticateRequest event, is that Session isn’t initialized when AuthenticateRequest is raised,

but it usually is initialized when AcquireRequestState is raised

The code first attempts to retrieve the principal object from Session This can result in anexception if Session doesn’t exist, and so the value would end up being null Also, if this is the

first page request by the user, the Session field will return null So the outcome is either a valid

PTPrincipal object or null

If the resulting principal value is null, PTPrincipal.Logout() is called to set the current principal

as an unauthenticated PTPrincipal, and the HttpContext is set to use that same principal object This

supports the idea of an unauthenticated anonymous guest user Both the web and business library

code have access to valid, if unauthenticated, principal objects, and can apply authorization code as

needed Additionally, by having the current principal be a valid PTPrincipal object, a remote data

portal can be invoked and the application server will impersonate the unauthenticated user identity

so that code can apply authorization rules as well.

On the other hand, if a principal object is retrieved from Session, then that value is set as thecurrent principal

Trang 13

Using Windows Integrated Security

If you wanted to use Windows integrated security, you wouldn’t need Login.aspx, the custom bership provider, or the code in Global.asax because the user’s identity is already known The userprovided his Windows credentials to the browser, which in turn provided them to the web server.This means that the virtual root in IIS must be configured to disallow anonymous users, thusforcing the user to provide credentials to access the site It is IIS that authenticates the user andallows authenticated users into the site

mem-To have ASP.NET use the Windows identity from IIS, web.config must be configured correctly:

<authentication mode="Windows"/>

<identity impersonate="true"/>

The authentication mode is set to Windows, indicating that ASP.NET should defer all tion to the IIS host Setting the impersonate property to true tells ASP.NET to impersonate the userauthenticated by IIS

authentica-■ Note If you use Windows integrated security, and you are using a remote data portal, you must make sure to

change the application server configuration file to also use Windows security If the data portal is hosted in IIS, thevirtual root must be set to disallow anonymous access, thereby forcing the client to provide IIS with the Windowsidentity from the web server via integrated security

Using the ASP.NET Membership Service

ASP.NET 2.0 not only supports the broad concept of membership as used previously, but it provides

a complete membership service, including all the code to make it work

The membership service is most often used with the SQL membership provider that comes withASP.NET This provider requires that you use a predefined database schema, along with the member-ship objects provided by Microsoft to manage and interact with the database By default, ASP.NETwill use a Microsoft SQL Server 2005 Express database in the virtual root’s App_Data directory, but youcan override that behavior to have it use another Microsoft SQL Server database if needed

The other membership provider shipped with ASP.NET is a connector to Active Directory It doesthe same thing, but stores the user information in AD instead of a SQL database

Using the Membership Service with a Local Data Portal

If you are running the data portal in the client process, you can use the SQL membership providerwithout any special effort In that case, the web server will interact directly with the database

Of course, you don’t need PTPrincipal or PTIdentity, because ASP.NET provides its own principaland identity types Similarly, you don’t need to manually handle the logout event of the LoginStatuscontrol or put any code in Global.asax

In short, it just works All the authorization code in CSLA NET will use the ASP.NET principalobject to call IsInRole(), so all the prebuilt authorization functionality just works

Using the Membership Service with a Remote Data Portal

Things are a bit more complex if you are using a remote data portal on an application server Thereare two things to consider here First, the SQL membership provider talks directly to the securitydatabase, knowing nothing about application servers If you want to use the application server, theapproach taken in PTWeb is better Second, the data portal will only accept principal objects thatinherit from Csla.Security.BusinessPrincipalBase, and of course the ASP.NET membershipprincipal types don’t do that

Trang 14

The first problem is one of application architecture, and you need to decide if it makes sensefor you to have the security mechanism talk directly to a database while your business code uses

an application server to talk to the business database

The second problem can be overcome with just a bit of code You need to wrap the ASP.NETmembership principal in a CSLA NET–style principal There are two parts to this First, you need

a custom principal class; second, you need to add some code to Global.asax

A custom principal class to wrap the ASP.NET principal object would look like this:

The code in Global.asax takes the ASP.NET principal and wraps it within a MembershipPrincipal:

protected void Application_AcquireRequestState(

object sender, System.EventArgs e){

Csla.ApplicationContext.User =new MembershipPrincipal(HttpContext.Current.User);

}

This code sets the ApplicationContext object’s User property to use the newMembershipPrincipal This way, the original user information and list of roles are preserved, but the

actual principal object used by the application inherits from BusinessPrincipalBase The result is

that the data portal can impersonate the web user on the application server

At this point, you should have an understanding of how the website is organized It referencesProjectTracker.Library and uses a master page and theme to provide a consistent, manageable

appearance for the site It also uses a mix of ASP.NET login controls and the prebuilt ProjectTracker

security objects to implement custom authentication

Now let’s move on and discuss the pages that provide actual business behaviors

Business Functionality

With the common functionality in the master page, Login.aspx, and Global.asax covered, it is

possible to move on to the business functionality itself As I mentioned earlier, I’ll walk through the

RolesEdit, ProjectList, and ProjectEdit web forms in some detail ResourceList and ResourcEdit

are available in the download and follow the same implementation approach

All of these web forms will be created using the new data binding capabilities built into ASP.NET2.0 and the CslaDataSource control discussed in Chapter 5 These capabilities allow the web developer

Trang 15

to easily link controls on the form to business objects and their properties The developer productivitygained through this approach is simply amazing.

Other key technologies I’ll be using are the MultiView control and the associated View control.These controls make it easy for a single page to present multiple views to the user, and are oftenvery valuable when building pages for editing data

Finally, remember that all these pages are content pages That means that they fit within thecontext of a master page—in this case, MasterPage.master As you’ll see, the tags in a content pageare a bit different from those in a simple web form

RolesEdit Form

The RolesEdit.aspx page is a content page, so its Page directive looks like this:

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master"

AutoEventWireup="true" CodeFile="RolesEdit.aspx.cs"

Inherits="RolesEdit" title="Project Roles" %>

Notice the MasterPageFile property, which points to MasterPage.master Also notice the Titleproperty, which sets the page’s title It is this value that is used in the master page’s Load event handler

to set the title text in the header region of the page

Figure 10-10 shows what the page looks like in Visual Studio

The grey Content title bar across the top of the main page body won’t be visible at runtime It isvisible at design time to remind you that you are editing a content area in the page If you look at thepage’s source, you’ll see that all the page content is contained within a Content control:

Figure 10-10.Layout of the RolesEdit page

Trang 16

The MultiView control contains two View controls, named MainView and InsertView Only one of

these views will be active (visible) at any time, so this form really defines two different views for the

user

Within your code, you select the view by setting the ActiveViewIndex property of the MultiViewcontrol to the numeric index of the appropriate View control Of course, using a numeric value like

this doesn’t lead to maintainable code, so within the page, I define an enumerated type with text

values corresponding to each View control:

private enum Views

{

MainView = 0, InsertView = 1 }

The Views type will be used to change the page view as needed

Error Label

Beneath the MultiView control in Figure 10-10 is a Label control with its ForeColor set to Red The

purpose behind this control is to allow the page to display error text to the user in the case of an

exception

As you’ll see, the data access code uses try catch blocks to catch exceptions that occur ing any data updates (insert, update, or delete) The text of the exception is displayed in ErrorLabel

dur-so it is visible to the user

Using a Business Object as a Data Source

In Chapter 5, I discussed the CslaDataSource control, and how it overcomes the limitations of the

standard ObjectDataSource control The RolesEdit page uses this control, making it relatively easy

to bind the Roles collection from ProjectTracker.Library to a GridView control on the page

The RolesDataSource data source control is defined on the page like this:

<csla:CslaDataSource ID="RolesDataSource" runat="server"

information so that it can load the Roles type and determine the properties that will be exposed by

child objects in the collection

Trang 17

OnDeleteObject and similar properties link the control to a set of event handlers in the page’scode The code in those event handlers interacts with the business object to perform each requestedaction.

Of course, to get this data source control onto the web form, you can simply drag theCslaDataSource control from the toolbox onto the designer surface and set its properties throughthe Properties window in Visual Studio

Then, when the GridView and DetailsView controls are placed on the form, you can use theirpop-up Tasks menu to select the data source control, as shown in Figure 10-11

You can either write the tags yourself, or use the designer support built into Visual Studio asyou choose

Caching the Object in Session

To optimize the performance of the website, business objects are stored in Session While they could

be retrieved directly from the database when needed, storing them in Session reduces the load on thedatabase server

To minimize the number of objects maintained in Session, all pages use the same Session field

to store their business objects: currentObject This way, only one business object is stored in Session

at any time, and that is the object being actively used by the current page

Of course, browsers have a Back button, which means that the user could navigate back to someprevious page that expects to be using a different type of object than the current page For instance,the user could be editing a Project object, and then start editing a Resource object Session wouldhave originally contained the Project, but then would contain the Resource

If the user then used the Back button to return to the ProjectEdit page, Session could still havethe Resource object in the currentObject field This possibility is very real, and must be dealt with bychecking the type of the object retrieved from Session to see if it is the type the page actually needs

If not, then the correct object must be retrieved from the database

In RolesEdit, the GetRoles() method performs this task:

private ProjectTracker.Library.Admin.Roles GetRoles()

{

object businessObject = Session["currentObject"];

if (businessObject == null ||

!(businessObject is ProjectTracker.Library.Admin.Roles)) Figure 10-11.Choosing a data source for a GridView or DetailsView

Trang 18

{ businessObject = ProjectTracker.Library.Admin.Roles.GetRoles();

Session["currentObject"] = businessObject;

} return (ProjectTracker.Library.Admin.Roles)businessObject;

}

The code retrieves the currentObject item from Session If the result is null, or if the resultingobject isn’t a Roles object, then a new Roles object is retrieved by calling the Roles.GetRoles()

factory method That newly retrieved object is placed in Session, making it the current object

In any case, a valid Roles object is returned as a result

Selecting an Object

The SelectObject event is raised when the web page needs data from the data source; the Roles

object in this case The page must handle the event and return the requested data object:

protected void RolesDataSource_SelectObject(

object sender, Csla.Web.SelectObjectArgs e) {

ProjectTracker.Library.Admin.Roles obj = GetRoles();

e.BusinessObject = obj;

}

The GetRoles() helper method is called to retrieve the Roles collection object Then the Rolesobject is returned to the RolesDataSource control by setting the e.BusinessObject property The data

source control then provides this object to the ASP.NET data binding infrastructure so it can be used

to populate any UI controls bound to the data control In this case, that’s the GridView control in

MainView That control is declared like this:

<asp:GridView ID="GridView1" runat="server"

The DataSourceID property establishes data binding to the RolesDataSource control

The DataKeyNames property specifies the name of the property on the business object that acts

as a primary key for the object For a Role object, this is Id Remember the use of the DataObjectField

attribute on the Id property in Chapter 8, which provides a hint to Visual Studio that this property

is the object’s unique key value

The first two columns in the GridView control are bound to properties from the data source:

Id and Name, respectively The third column is a CommandField, which automatically adds Delete and

Edit links next to each element in the list The Delete link automatically triggers the DeleteObject

to delete the specified object The Edit link puts the row into in-place edit mode, allowing the user to

edit the data in the selected row If the user accepts their updates, the UpdateObject event is

auto-matically raised No code beyond that handling those events is required to support either of these

links

Trang 19

Of course, you don’t have to deal with all these tags if you don’t want to Most of the code in theCslaDataSource control exists to support the graphical designer support in Visual Studio Look back atFigure 10-10 and notice how the GridView control displays the Id, Name, and command columns I con-figured the control entirely using the Visual Studio designer and setting properties on the controls.Figure 10-12 shows the Fields dialog for the GridView control.

Notice that the Available fields box contains a list of the potentially bound fields from the datasource: Id and Name The CslaDataSource control’s designer support returns this list by using reflec-tion against the data source object as discussed in Chapter 5 You can use this dialog to choose whichcolumns are displayed, to control the way they are displayed, to rearrange their order, and more

Inserting an Object

The MainView contains not only a GridView control, but also a LinkButton control named

AddRoleButton This button allows the user to add a new Role object to the Roles collection To dothis, the current View is changed to InsertView:

protected void AddRoleButton_Click(object sender, EventArgs e)

{

this.DetailsView1.DefaultMode = DetailsViewMode.Insert;

MultiView1.ActiveViewIndex = (int)Views.InsertView;

}

This changes the page to appear, as shown in Figure 10-13

Look at the address bar in the browser; see how it is still RolesEdit.aspx even though the play is entirely different from Figure 10-10 This illustrates the power of the MultiView control, whichallows a user to remain on a single page to view, edit, and insert data

dis-Figure 10-12.Fields dialog for a GridView control

Trang 20

The control shown here is a DetailsView control, which is data bound to the sameRolesDataSource control as the GridView earlier This control is declared in a manner very similar to

columns are defined in a GridView

If the user enters values for a new role and clicks the Insert link in the DetailsView control, theInsertObject event is raised by RolesDataSource This event is handled in the page to add the new

role to the Roles collection:

protected void RolesDataSource_InsertObject(

object sender, Csla.Web.InsertObjectArgs e) {

try { ProjectTracker.Library.Admin.Roles obj = GetRoles();

ProjectTracker.Library.Admin.Role role = obj.AddNew();

Figure 10-13.The RolesEdit.aspx page when a new role is being added

Trang 21

Csla.Data.DataMapper.Map(e.Values, role);

Session["currentObject"] = obj.Save();

e.RowsAffected = 1;

} catch (Csla.DataPortalException ex) {

this.ErrorLabel.Text = ex.BusinessException.Message;

e.RowsAffected = 0;

} catch (Exception ex) {

this.ErrorLabel.Text = ex.Message;

e.RowsAffected = 0;

} }

This code retrieves the current Roles object and then calls its AddNew() method to add a newchild Role object Recall that in Chapter 8 the AddNewCore() method was implemented to enableeasy adding of child objects to the collection The public AddNew() method ultimately results in

a call to AddNewCore(), which adds an empty child object to the collection

This new child object is populated with data using the DataMapper object from Chapter 5:Csla.Data.DataMapper.Map(e.Values, role);

All new values entered by the user are provided to the event handler through e.Values TheMap() method uses reflection to copy those values to the corresponding properties on the object

If you want to avoid this use of reflection, you can replace this line with code like this:

to commit all changes

Once the Save() method is complete, the resulting (updated) Roles object is put into Session

This is very important because the result of Save() is a new Roles object, and that new object must

be used in place of the previous one on subsequent pages For instance, the newly added role datagenerated a new timestamp value in the database, which can only be found in this new Roles object.This completes the insert operation, but the MultiView control is still set to display theInsertView It needs to be reset to display MainView That is done by handing the ItemInserted eventfrom the DetailsView control:

protected void DetailsView1_ItemInserted(

object sender, DetailsViewInsertedEventArgs e) {

MultiView1.ActiveViewIndex = (int)Views.MainView;

this.GridView1.DataBind();

}

Trang 22

The ActiveViewIndex is changed so that the MainView is displayed when the page refreshes Also,the GridView control in MainView is told to refresh its data by calling its DataBind() method.

Calling DataBind() causes the GridView to refresh its display so it shows the newly added Roleobject Behind the scenes, this triggers a call to RolesDataSource, causing it to raise its SelectObject

event

Figure 10-13 also shows a Cancel link If the user clicks that link, she likewise needs to be returned

to MainView When the user clicks Cancel, it triggers a ModeChanged event on the DetailsView control:

protected void DetailsView1_ModeChanged(

object sender, EventArgs e) {

As shown in Figure 10-10, the CommandField column in the GridView control includes both Delete

and Edit links for each row I’ll get to the Delete link shortly, but for now let’s focus on the Edit link

When the user clicks the Edit link on a row, the GridView allows the user to edit that row’s data, as

shown in Figure 10-14

The user can edit the Name column only The Id column is set to read-only:

<asp:BoundField DataField="Id" HeaderText="Id"

ReadOnly="True" SortExpression="Id" />

Figure 10-14.The RolesEdit.aspx page when a role is being edited

Trang 23

When done, the user can either click the Update or Cancel links on the row If they click Update,the UpdateObject event is raised by RolesDataSource to trigger the data update This event is handled

in the page:

protected void RolesDataSource_UpdateObject(

object sender, Csla.Web.UpdateObjectArgs e) {

try { ProjectTracker.Library.Admin.Roles obj = GetRoles();

ProjectTracker.Library.Admin.Role role = obj.GetRoleById(int.Parse(e.Keys["Id"].ToString()));

role.Name = e.Values["Name"].ToString();

Session["currentObject"] = obj.Save();

e.RowsAffected = 1;

} catch (Csla.DataPortalException ex) {

this.ErrorLabel.Text = ex.BusinessException.Message;

e.RowsAffected = 0;

} catch (Exception ex) {

this.ErrorLabel.Text = ex.Message;

e.RowsAffected = 0;

} }

This code is quite similar to that for the insert operation discussed earlier, though in this case,the specific Role object that was edited is retrieved from the collection:

ProjectTracker.Library.Admin.Role role =obj.GetRoleById(int.Parse(e.Keys["Id"].ToString()));

e.Keys contains all the values from the page that correspond to the properties defined in theGridView control’s DataKeyNames property Recall that the only property set in DataKeyNames was Id,

so that’s the only value provided through e.Keys This value is passed to the GetRoleById() method

to retrieve the correct Role object

Note Update and delete operations require that appropriate business object property names be specified in

the GridViewor DetailsViewcontrol’s DataKeyNamesproperty

Since only one property can be edited, I opted not to use DataMapper and to set the propertyvalue manually However, in a more complex edit scenario in which many properties are edited, you may choose to use DataMapper to simplify the code

Finally, the Roles object’s Save() method is called to commit the user’s changes to the database

As with the insert process, the new Roles object returned from Save() is put into Session for use onall subsequent page requests

Trang 24

protected void RolesDataSource_DeleteObject(

object sender, Csla.Web.DeleteObjectArgs e) {

try { ProjectTracker.Library.Admin.Roles obj = GetRoles();

this.ErrorLabel.Text = ex.BusinessException.Message;

e.RowsAffected = 0;

} catch (Exception ex) {

this.ErrorLabel.Text = ex.Message;

e.RowsAffected = 0;

} }

The Id value for the Role object to delete is retrieved from e.Keys and used to call the Remove()method on the Roles collection Recall from Chapter 8 that this overload of Remove() accepts the

Id value of the Role object

Of course, the child object is merely marked for deletion, and isn’t removed until the Save()method is called on the Roles object itself Again, the resulting Roles object returned from

Save() is put into Session for use on subsequent page requests

At this point, you should understand the basic process for creating a grid-based data form thatsupports viewing, inserting, editing and deleting data The only thing left to do in RolesEdit is to add

support for authorization

Authorization

The RolesEdit authorization code is perhaps the simplest in the application If the user isn’t authorized

to edit the Roles object, then the CommandField column in the GridView control shouldn’t be shown;

and if the user can’t add a new role, then the LinkButton for adding a new object shouldn’t be shown

When the page is loaded, an ApplyAuthorizationRules() method is called:

protected void Page_Load(object sender, EventArgs e)

{

if (!IsPostBack) ApplyAuthorizationRules();

else this.ErrorLabel.Text = "";

this.AddRoleButton.Visible = ProjectTracker.Library.Admin.Roles.CanAddObject();

}

Trang 25

The ApplyAuthorizationRules() method asks the Roles class whether the current user isauthorized to edit the object or add new roles If the user isn’t authorized, then the appropriatecontrols Visible properties are set to false, and the controls are thereby hidden

Since the user is then unable to put the GridView control into edit mode or ask it to delete anitem, the display effectively becomes read-only Similarly, without the LinkButton for adding a newitem, the user can’t switch the MultiView to InsertView; so again the page becomes a simple read-only page

As you can see, creating a simple grid-based edit page requires relatively little work You add

a data control, bind the GridView and possibly a DetailsView control to the data, and write a bit ofcode Most of the code in this page exists to react to user actions as they indicate that data is to beinserted, edited, or deleted

ProjectList Form

The ProjectList web form is responsible for displaying the list of projects to the user and allowingthe user to choose a specific project to view or edit From this page, the user can also delete a proj-ect and choose to add a new project Figure 10-15 shows the layout of ProjectList

It is important to realize that the GridView control actually has three columns: Id, Name, and the

CommandField column with the Delete links:

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