Pro ASP.NET Extensibility
Jörg Krause
Pro ASP.NET Extensibility Copyright © 2009 by Jörg Krause All rights reserved...
78 downloads
1108 Views
5MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Pro ASP.NET Extensibility
Jörg Krause
Pro ASP.NET Extensibility Copyright © 2009 by Jörg Krause All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. ISBN-13 (pbk): 978-1-4302-1983-5 ISBN-13 (electronic): 978-1-4302-1984-2 Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1 Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. Lead Editor: Jonathan Hassell Technical Reviewer: Stefan Turalski Editorial Board: Clay Andres, Steve Anglin, Mark Beckner, Ewan Buckingham, Tony Campbell, Gary Cornell, Jonathan Gennick, Jonathan Hassell, Michelle Lowman, Matthew Moodie, Jeffrey Pepper, Frank Pohlmann, Douglas Pundick, Ben Renow-Clarke, Dominic Shakeshaft, Matt Wade, Tom Welsh Project Manager: Kylie Johnston Copy Editor: Katie Stence Associate Production Director: Kari Brooks-Copony Production Editor: Laura Esterman Compositor: Diana Van Winkle Proofreader: Dan Shaw Indexer: BIM Indexing & Proofreading Services Artist: April Milne Cover Designer: Kurt Krames Manufacturing Director: Tom Debolski Distributed to the book trade worldwide by Springer-Verlag New York, Inc., 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax 201-348-4505, e-mail kn`ano)juktnqj]p9oanranE@9ptpJ]ia+: 8^n+: =naukq_]hha`] 8]ol6@nkl@ksjHeopnqj]p9oanranE@9``hSd]p: 8]ol6HeopEpaiR]hqa9COaha_pa`9Pnqa:Caag8+]ol6HeopEpai: 8]ol6HeopEpaiR]hqa9J:Jan`8+]ol6HeopEpai: 8]ol6HeopEpaiR]hqa9S:@kj#pgjks8+]ol6HeopEpai: 8+]ol6@nkl@ksjHeop: 8^n+: 8]ol6>qppkjnqj]p9oanranE@9^pjOaj`Patp9Oaj`+: 8+`er: 8+bkni: 8+^k`u: 8+dpih: &IGURE SHOWSHOWITWILLLOOKLIKEINTHE6ISUAL3TUDIODESIGNER
C H A P T E R 1 N U N D E R S T A N D I N G A S P . N E T
Figure 1-12. Simple page in Visual Studio Designer 4HE!30.%4ENGINEGENERATESTHEOUTPUTCODE AFRAGMENTOFWHICHISSHOWNIN,ISTING Listing 1-9. Simple ASPX Page from Last Listing Compiled into Code in Debug Mode lq^he__h]oo`ab]qhp[]olt6[@ab]qhp(ENamqenaoOaooekjOp]pa(EDpplD]j`han w lner]paop]pe_k^fa_p[[beha@alaj`aj_eao7 lner]paop]pe_^kkh[[ejepe]heva`7 W@a^qccanJkjQoan?k`aY lq^he_`ab]qhp[]olt$% w ^]oa*=llNah]peraRenpq]hL]pd9z+@ab]qhp*]olt7 eb$[[ejepe]heva`% w opnejcWYrenpq]hBeha@alaj`aj_eao9jasopnejcWYwz+@ab]qhp*]olty7 [[beha@alaj`aj_eao9± ^]oa*CapSn]lla`Beha@alaj`aj_eao$renpq]hBeha@alaj`aj_eao%7 [[ejepe]heva`9pnqa7 y y W@a^qccanJkjQoan?k`aY lner]paDpihPepha[[>qeh`?kjpnkh[[_kjpnkh.$% w napqnjjasDpihPepha$%7 y W@a^qccanJkjQoan?k`aY lner]parke`[[>qeh`?kjpnkh[[_kjpnkh/$HeopEpai?khha_pekj[[_pnh% w HeopEpaiepai9pdeo*[[>qeh`?kjpnkh[[_kjpnkh0$%7 [[_pnh*=``$epai%7 HeopEpaiepai.9pdeo*[[>qeh`?kjpnkh[[_kjpnkh1$%7 [[_pnh*=``$epai.%7 HeopEpaiepai/9pdeo*[[>qeh`?kjpnkh[[_kjpnkh2$%7 [[_pnh*=``$epai/%7 y W@a^qccanJkjQoan?k`aY lner]paHeopEpai[[>qeh`?kjpnkh[[_kjpnkh0$% w HeopEpaiepai9jasHeopEpai$%7 epai*R]hqa9C7 epai*Oaha_pa`9pnqa7 epai*Patp9Caag7
35
36
C H APT ER 1 N UNDER S TA NDING A S P . NET
napqnjepai7 y W@a^qccanJkjQoan?k`aY lner]paHeopEpai[[>qeh`?kjpnkh[[_kjpnkh1$% w HeopEpaiepai9jasHeopEpai$%7 epai*R]hqa9J7 epai*Patp9Jan`7 napqnjepai7 y W@a^qccanJkjQoan?k`aY lner]paHeopEpai[[>qeh`?kjpnkh[[_kjpnkh2$% w HeopEpaiepai9jasHeopEpai$%7 epai*R]hqa9S7 epai*Patp9@kj#pgjks7 napqnjepai7 y W@a^qccanJkjQoan?k`aY lner]pa>qppkj[[>qeh`?kjpnkh^pjOaj`$% w >qppkj^qppkj9jas>qppkj$%7 ^]oa*^pjOaj`9^qppkj7 ^qppkj*=llhuOpuhaOdaapOgej$pdeo%7 ^qppkj*E@9^pjOaj`7 ^qppkj*Patp9Oaj`7 napqnj^qppkj7 y W@a^qccanJkjQoan?k`aY lner]pa@nkl@ksjHeop[[>qeh`?kjpnkh``hSd]p$% w @nkl@ksjHeopheop9jas@nkl@ksjHeop$%7 ^]oa*``hSd]p9heop7 heop*=llhuOpuhaOdaapOgej$pdeo%7 heop*E@9``hSd]p7 pdeo*[[>qeh`?kjpnkh[[_kjpnkh/$heop*Epaio%7 napqnjheop7 y W@a^qccanJkjQoan?k`aY lner]paDpihBkni[[>qeh`?kjpnkhbkni-$% w DpihBknibkni9jasDpihBkni$%7 ^]oa*bkni-9bkni7 bkni*E@9bkni-7 EL]noan=__aookn]__aookn9bkni7 ]__aookn*=``L]noa`Oq^K^fa_p$± jasHepan]h?kjpnkh$XnXj8`er:XnXj%%7 Patp>kt^kt9pdeo*[[>qeh`?kjpnkhptpJ]ia$%7 ]__aookn*=``L]noa`Oq^K^fa_p$^kt%7 ]__aookn*=``L]noa`Oq^K^fa_p$± jasHepan]h?kjpnkh$XnXj8^n+:XnXj± =naukq_]hha`]XnXj%%7
C H A P T E R 1 N U N D E R S T A N D I N G A S P . N E T
@nkl@ksjHeopheop9pdeo*[[>qeh`?kjpnkh``hSd]p$%7 ]__aookn*=``L]noa`Oq^K^fa_p$heop%7 ]__aookn*=``L]noa`Oq^K^fa_p$± jasHepan]h?kjpnkh$XnXj8^n+:XnXj%%7 >qppkj^qppkj9pdeo*[[>qeh`?kjpnkh^pjOaj`$%7 ]__aookn*=``L]noa`Oq^K^fa_p$^qppkj%7 ]__aookn*=``L]noa`Oq^K^fa_p$± jasHepan]h?kjpnkh$XnXj8+`er:XnXj%%7 napqnjbkni7 y W@a^qccanJkjQoan?k`aY lner]paDpihDa]`[[>qeh`?kjpnkhDa]`-$% w DpihDa]`da]`9jasDpihDa]`$da]`%7 ^]oa*Da]`-9da]`7 da]`*E@9Da]`-7 DpihPephapepha9pdeo*[[>qeh`?kjpnkh[[_kjpnkh.$%7 EL]noan=__aookn]__aookn9da]`7 ]__aookn*=``L]noa`Oq^K^fa_p$pepha%7 napqnjda]`7 y W@a^qccanJkjQoan?k`aY lner]parke`[[>qeh`?kjpnkhPnaa$`ab]qhp[]olt[[_pnh% w pdeo*Ejepe]heva?qhpqna$%7 EL]noan=__aookn]__aookn9[[_pnh7 ]__aookn*=``L]noa`Oq^K^fa_p$± jasHepan]h?kjpnkh$XnXjXnXj8@K?PULAdpihLQ>HE?X)++S/?++@P@TDPIH-*,± Pn]joepekj]h++AJXXdppl6++sss*s/*knc+PN+tdpih-+@P@+tdpih-)± pn]joepekj]h*`p`X:XnXjXnXj8dpihtihjo9Xdppl6++sss*s/*knc+-555+tdpihX:XnXj%%7 DpihDa]`da]`9pdeo*[[>qeh`?kjpnkhDa]`-$%7 ]__aookn*=``L]noa`Oq^K^fa_p$da]`%7 ]__aookn*=``L]noa`Oq^K^fa_p$jasHepan]h?kjpnkh$XnXj8^k`u:XnXj± 8d-:XnXj=lnaoo)ReasOp]pa8+d-:XnXj%%7 DpihBknibkni9pdeo*[[>qeh`?kjpnkhbkni-$%7 ]__aookn*=``L]noa`Oq^K^fa_p$bkni%7 ]__aookn*=``L]noa`Oq^K^fa_p$± jasHepan]h?kjpnkh$XnXj8+^k`u:XnXj8+dpih:XnXj%%7 y W@a^qccanJkjQoan?k`aY lner]paPatp>kt[[>qeh`?kjpnkhptpJ]ia$% w Patp>kt^kt9jasPatp>kt$%7 ^]oa*ptpJ]ia9^kt7 ^kt*=llhuOpuhaOdaapOgej$pdeo%7 ^kt*E@9ptpJ]ia7 napqnj^kt7 y W@a^qccanJkjQoan?k`aY lnkpa_pa`kranne`arke`Bn]iaskngEjepe]heva$% w ^]oa*Bn]iaskngEjepe]heva$%7 pdeo*[[>qeh`?kjpnkhPnaa$pdeo%7
37
38
C H APT ER 1 N UNDER S TA NDING A S P . NET
^]oa*=``Sn]lla`Beha@alaj`aj_eao$[[beha@alaj`aj_eao%7 ^]oa*Namqaop*R]he`]paEjlqp$%7 y W@a^qccanJkjQoan?k`aY lq^he_kranne`aejpCapPulaD]od?k`a$% w napqnj)-234/4305-7 y W@a^qccanJkjQoan?k`aY lq^he_kranne`arke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w ^]oa*Lnk_aooNamqaop$_kjpatp%7 y ++Lnklanpeao lnkpa_pa`Dppl=llhe_]pekj=llhe_]pekjEjop]j_a w cap w napqnjpdeo*?kjpatp*=llhe_]pekjEjop]j_a7 y y lnkpa_pa`@ab]qhpLnkbehaLnkbeha w cap w napqnj$@ab]qhpLnkbeha%pdeo*?kjpatp*Lnkbeha7 y y y The code is not very readable, but this is what generated code is supposed to look like, right? There is no need to do anything special with it, but rather to learn what is going on internally. Note two things here. First, the declarative assignment of attributes is being replaced with simple prop erty assignments. See the following markup: 8]ol6>qppkjpatp9Oaj`: It becomes this in the generated class: ^qppkj-*Patp9Oaj` Second, all literal stuff is placed in strings and copied into the final page “as is,” which is shown in the following snippet: jasHepan]h?kjpnkh$XnXj8+^k`u:XnXj8+dpih:XnXj%% Once the class is complete, the engine proceeds to the next step.
C H A P T E R 1 N U N D E R S T A N D I N G A S P . N E T
Step Two—Initializing After creating the control hierarchy, all the controls of the page are moved into the initialization state. This becomes apparent by raising the Ejep event. In this phase not only are the controls ready, but they also have their static, declarative properties assigned.
NNote
“Declarative” is everything that’s written within the markup and not in code fragments.
Whether or not a property has been declaratively assigned, you still can change the value any time BEFORETHERENDERPROCESSISCOMPLETE(OWEVER BECAUSE)MTALKINGABOUTVIEWSTATE THEINITIALIZA tion step plays a special role regarding property values.
View State Tracks Changes View state tracks changes of properties. This is a special function of the Op]pa>]c class defined in the Ouopai*Sa^*QE namespace, which holds the data behind the scenes. Tracking starts during the Initializing step of the page. In order to recognize the default value and the changed one, the Op]pa>]c compares the initialized value set by declarative markup with the current value set any time later programmatically. Even if the results look the same, the view state treats the data in a different way.
View State and Dynamic Controls Using dynamic controls, you’ll know that technically it’s possible to add controls any time before the page render process is complete. The view state is not that flexible. Adding controls using ?kjpnkho*=`` method must take place in the initializing phase. Otherwise, the Op]pa>]c class does not recognize the Ejep event at the right time and does not start tracking changes. You still can add controls anytime, but for those controls the view state will not work. You might find problems here OCCASIONALLY ASTHEMAJORITYOFCONTROLSBEINGADDEDDYNAMICALLYSUCHASTHEH]^ahCONTROLDONT require property value tracking. The golden rule states that in order to “take care of the view state, whether it’s required or not, add dynamic controls during the Initializing step.”
Step Three—Loading the View State View state data is saved into a hidden field called [[REASOP=PA. It’s transmitted back to the server only during a POST operation, which forces a postback.
NNote
You can completely ignore page requests that use GET for this step. Such requests do not interact with view state at all.
In this step the engine decodes values from the hidden field and assigns them to the controls, looping recursively through the control hierarchy. Also in this phase the validity of the view state is checked. There are several reasons why the view state could become invalid. One reason is that the control hierarchy has been changed during postback and the control is not at the expected place, or removed completely.
39
40
C H APT ER 1 N UNDER S TA NDING A S P . NET
)FEVERYTHINGISFINE THEENGINEPROCEEDSTOTHENEXTSTEP3EETHESECTIONh6IEW3TATE!NTI Patterns” for an example of what’s going on when it went wrong.
Step Four—Loading Postback Data After loading and restoring the view state, the form data is processed. Not all controls can return data. To let the engine know whether it has to look for data sent back, the ELkop>]_g@]p]D]j`han interface is recognized. Each control implementing this interface might send form data back. Form DATAISSENTBACKINTHE(440PROTOCOLASIDnVALUEPAIRS Iu_heajpe`9r]hqa The page class looks for a control with a ?heajpE@ that equals “Myclientid”. When found, it checks whether the control implements the ELkop>]_g@]p]D]j`han interface. If it does this, the page CLASSCALLSTHEONLYMETHODDEFINEDTHEREHk]`Lkop@]p]. That means that the control itself manages the loading of data, which opens up a great way of changing behavior and adding custom code. Concerning view state, this behavior is very significant. The ability to get the values of controls back between postbacks is called “sticky form” behavior. It’s one of the best features of ASP.NET. Let me explain this in more detail using the example of a Patp>kt control. The Patp>kt has a property Patp. It also implements the ELkop>]_g@]p]D]j`han interface. Once a page with the textbox control is posted back, the Hk]`Lkop@]p] method reads the value of the control out of the view state and writes it into the PatpPROPERTY4HEDEFAULTVALUETHATWASPROBABLYSETDECLARATIVELYISOVERWRITTEN9OU can easily check this with the following definition: 8]ol6Patp>ktnqj]p9oanranE@9ptpJ]iaPatp9+: 4HERENDERPROCESSTRANSFORMSTHISMARKUPINTO(4-,FORM 8ejlqppula9patpe`9ptpJ]iaj]ia9ptpJ]ia+: If the user now enters some text here, like “It’s a geek”, the form transmitted back to the server contains at least this line: ptpJ]ia9Ep#o]caag This data pair becomes part of the Namqaop*Bkni collection. The key is “txtName” and the value is “It’s a geek”. The page class hands this data pair to the Hk]`Lkop@]p] method. Internally there is nothing surprising here. Using Reflector, you can look into the relevant code inside Ouopai*Sa^*`hh SEE,ISTING Listing 1-10. Disassembled Code of the LoadPostData Method lnkpa_pa`renpq]h^kkhHk]`Lkop@]p]$opnejclkop@]p]Gau(± J]iaR]hqa?khha_pekjlkop?khha_pekj% w ^]oa*R]he`]paArajp$lkop@]p]Gau%7 opnejcpatp9pdeo*Patp7 opnejcopn.9lkop?khha_pekjWlkop@]p]GauY7 eb$pdeo*Na]`Kjhu""patp*Amq]ho$opn.(Opnejc?kil]neokj*Kn`ej]h%% w pdeo*Patp9opn.7 napqnjpnqa7 y napqnjb]hoa7 y
C H A P T E R 1 N U N D E R S T A N D I N G A S P . N E T
This code snippet is from Patp>kt, and the one and only property filled by a postback is Patp. 4HECONTROLMIGHTHAVEBEENSETREAD ONLYANDTHEREFORETHISISCHECKEDFIRSTpdeo*Na]`Kjhu). Also, as the code compares the old value and the new one, it’s written only if the value has been changed. Why is this additional check required? You might assume that comparing takes more time than ASSIGNINGAPROPERTYEVENIFTHEREISNOTHINGTORENDERATTHISSTAGE7ELL THEREASONISVIEWSTATE To understand this, take a look into the code for the PatpPROPERTYIN,ISTING Listing 1-11. Disassembled Code of Text Method of the TextBox Class lq^he_renpq]hopnejcPatp w cap w opnejcopn9$opnejc%pdeo*ReasOp]paWPatpY7 eb$opn9jqhh% w napqnjopn7 y napqnjopnejc*Ailpu7 y oap w pdeo*ReasOp]paWPatpY9r]hqa7 y y The value is not stored in a private field, but only in view state. The tracking feature of the underlying Op]pa>]c class handles changes as well. If nothing has changed, it’s not recommended to assign the value to view state, or the Op]pa>]c class will start tracking. If there is nothing to track, it doesn’t make sense to do so, and the Hk]`Lkop@]p] method is clever enough to know. One major aspect many developers overlook is the role of view state in the handling of postback data. The form data loading procedures do not involve view state. More than that, they try to avoid any interaction with view state. The form data handling and stickiness behavior has nothing to do with the view state. The stickiness is a feature that comes with the ELkop>]_g@]p]D]j`han interface. *USTFORCLARITY ,ISTING SHOWSITINITSENTIREGLORY Listing 1-12. Code of IPostBackDataHandler Interface lq^he_ejpanb]_aELkop>]_g@]p]D]j`han w ^kkhHk]`Lkop@]p]$opnejclkop@]p]Gau(± J]iaR]hqa?khha_pekjlkop?khha_pekj%7 rke`N]eoaLkop@]p]?d]jca`Arajp$%7 y You know now that view state tracks changes and stores these changes. You know, too, that the stickiness of the form has nothing to do with view state. The Hk]`Lkop@]p] method just explores the values sent back to server and restores it into the appropriate properties. .OW ALLTHECONTROLSHAVEBEENINITIALIZED PROPERLYFILLEDWITHDECLARATIVEDATA OVER WRITTEN with data posted back from the browser, and are ready to enter the next state.
41
42
C H APT ER 1 N UNDER S TA NDING A S P . NET
Step Five—Loading Step Most descriptions of ASP.NET put user code into the Hk]` event. In the explanation of life cycles, you read about this important step because it signals to us that the control is now “ready.” Even if you don’t want to do anything else with the control, it’s ready to be rendered. This is why the best step for adding custom code and modifying the behavior of the page is the load step. Internally, it’s much easier, because there is nothing to do within the control. Anything that is supposed to happen here is up to you.
Step Six—Raising Postback Events Several controls can fire events, depending on the form data posted back. After the loading step, the events are fired one by one. For example, the >qppkj control can fire a ?he_g event, whereas the @nkl@ksjHeop fires Oaha_pa`Ej`at?d]jca`. This is another major feature that makes the dynamic portion of an ASP.NET page so powerful and easy to program. There are two kinds of events fired during the page’s postback. One kind is responsible for changes, indicated by the suffix [?d]jca. The implementation of ELkop>]_g@]p]D]j`han is responsible for recognizing this and firing the appropriate event by calling the N]eoaLkop@]p]?d]jca`Arajp method. In the case of the Patp>kt example, the event exposed by the class is KjPatp?d]jca`. The other kind of event is the trigger event. For example, in the case of a >qppkj this is a ?he_g EVENT4HEREISNOSPECIALSTATEREQUIREDANDNODATATOCOMPARE!SYOUCANSEEIN,ISTING TO handle trigger events the ELkop>]_gArajpD]j`han interface is used. Listing 1-13. Code of IPostBackEventHandler Interface lq^he_ejpanb]_aELkop>]_gArajpD]j`han w rke`N]eoaLkop>]_gArajp$opnejcarajp=ncqiajp%7 y To understand what happens internally, we’ll look into the N]eoaLkop>]_gArajp method. ,ISTING ISFORA>qppkj control. Listing 1-14. Code of Typical Implementation of IPostBackEventHandler Interface lnkpa_pa`renpq]hrke`N]eoaLkop>]_gArajp$opnejcarajp=ncqiajp% w ^]oa*R]he`]paArajp$pdeo*QjemqaE@(arajp=ncqiajp%7 eb$pdeo*?]qoaoR]he`]pekj% w pdeo*L]ca*R]he`]pa$pdeo*R]he`]pekjCnkql%7 y pdeo*Kj?he_g$Arajp=nco*Ailpu%7 pdeo*Kj?kii]j`$jas?kii]j`Arajp=nco$ pdeo*?kii]j`J]ia(pdeo*?kii]j`=ncqiajp%%7 y !SYOUCANSEE THISMETHODRAISESMORETHANONEEVENTINAPREDEFINEDANDHARD CODEDORDER 3EVERALOTHERACTIONSTAKEPLACEFIRSTSUCHASVALIDATIONBUTTHEEVENTSBEINGFIREDAREUNCONDI tional. Again, all of these events can only happen during a postback. Calling the page using GET will do nothing. If you have a @nkl@ksjheop, and if you change the current index programmatically, nothing will happen unless the page is posted back. This is different from the windows programming model, where the controls can fire events immediately and independently of a particular state.
C H A P T E R 1 N U N D E R S T A N D I N G A S P . N E T
Step Seven—Storing the View State After all events have been fired, the current state of each control’s property changes needs to be stored. The tracked changes are retrieved recursively through the controls hierarchy. To get the value back, the O]raReasOp]pa method of each control is called. The collection of data is then serial IZEDANDENCODEDUSING"ASE In the next step, the string is saved to the hidden field with id [[REASOP=PA.
Step Eight—Rendering the Page The render process runs through all the controls and allows them to render. This is done by a recursive call to all Naj`an?kjpnkh methods. The DpihBkni control mentioned at the beginning is responsible for creating the [[REASOP=PA hidden field, which is the container for view state data.
The True Role of View State The eight states of the life cycle are virtually all important for view state. There are several more steps the various life cycles could run through, but these steps don’t affect view state, so I’ll exclude them for now. To understand the view state, it’s crucial to know what the purpose of view state is. As the name implies, view state stores status information. But what kind of status is it? The hierarchy of controls and the default values of properties are defined in the declarative part of the page. Take a look at this markup: 8]ol6H]^ahnqj]p9oanranPatp9Saha]njreasop]paBkjp)>kh`9pnqa+: Neither the text “We learn viewstate” nor the value >kh` of the Bkjp property is stored in view state. The values are assigned during the initializing phase. View state, in contrast, tracks changes made programmatically. This leads to the first definition about view state.
NNote
View state becomes important only if the page contains custom code.
(OWEVER IFAPAGECONTAINSCUSTOMCODE THISDOESNOTNECESSARILYMEANTHATVIEWSTATEISREQUIRED ,ETSLOOKINTOANOTHERCODESNIPPET4HEFOLLOWINGEXAMPLEIN,ISTING HASTWOBUTTONS"OTHCAUSE a postback, but only one has an Kj?he_g event attached and handled to access properties in code. Listing 1-15. Markup of the View State Test 8]ol6H]^ahE@9H]^ahIaoo]canqj]p9oanranPatp9Saha]njreasop]pa:8+]ol6H]^ah: 8^n+: 8]ol6>qppkjE@9>qppkjOq^iepnqj]p9oanrankj_he_g9>qppkjOq^iep[?he_g Patp9?d]jcaPatpSe`pd9-1,lt+: 8^n+: 8]ol6>qppkjE@9>qppkjAilpunqj]p9oanranPatp9Jk?d]jca Se`pd9-1,lt+: !SSHOWNIN,ISTING THECODEOFTHECLICKHANDLERISSIMPLE TOO Listing 1-16. Code of the Click Handler lnkpa_pa`rke`>qppkjOq^iep[?he_g$k^fa_poaj`an(Arajp=ncoa% w H]^ahIaoo]ca*Patp9DahhkCaag7 y
43
44
C H APT ER 1 N UNDER S TA NDING A S P . NET
!TTHEFIRSTCALLOFTHEPAGETHE!308MARKUPISPROCESSEDANDALLVALUESOFPROPERTIESARESET especially the text “We learn viewstate” for the H]^ah. View state stores nothing and therefore con tains only internal information without custom data: 8ejlqppula9de``ajj]ia9[[REASOP=PAe`9[[REASOP=PA± r]hqa9+sAL@sQHHPAtJfIvJ@EtJfNgVCj1]if>oKK]l2?rN^lQI1@5Ihck+: Now click on the button “No Change”. This forces the page to postback and reload, but nothing more happens, because no custom code is involved. Now click on the other button “Change Text”. Again a postback occurs, but now the handler processes the Kj?he_g event. In the code, the text of the label gets a new value. View state is changed to this: 8ejlqppula9de``ajj]ia9[[REASOP=PAe`9[[REASOP=PAr]hqa9+sAL@sQHHPAtJfIvJ@Et fMLV>U?=cILV>U?=cAL@tU?DcNQVTd,>MpEUSto^u>DVSRnESNgVDPaJ--HePr1>F,tO`au,H,Mojg4+: As you see, the coded part is bigger and obviously contains more information. This is no sur prise. If you click now on the other button “No Change”, the page loads as expected and no custom CODEISRUN(OWEVER THETEXTOFTHELABELISSTILLh(ELLOGEEKv4HEREISNOCUSTOMCODETHEINITIAL izing phase has obviously passed and has set the declaratively assigned values. But the change made PROGRAMMATICALLYINANEARLIERPOSTBACKISSTILLTHEREITSPERSISTENT That’s really the purpose of view state. During postbacks, it makes programmatic changes to control properties persistent. To check whether this behavior is really managed by view state, just disable it using the ]c classWILLNEVERMISSANYTHING(OWEVER THISPOINTIS critical. You might experience the following exception loading the page after postback: WB]eha`hk]`ejcreasop]paY B]eha`pkhk]`reasop]pa*Pda_kjpnkhpnaaejpksde_dreasop]paeo^aejchk]`a`iqop i]p_dpda_kjpnkhpnaapd]ps]oqoa`pko]rareasop]pa`qnejcpdalnarekqonamqaop*Bkn at]ilha(sdaj]``ejc_kjpnkho`uj]ie_]hhu(pda_kjpnkho]``a``qnejc]lkop)^]_giqop i]p_dpdapula]j`lkoepekjkbpda_kjpnkho]``a``qnejcpdaejepe]hnamqaop* Let’s create an example so that you can figure out what is happening internally: lnkpa_pa`rke`L]ca[Ejep$k^fa_poaj`an(Arajp=ncoa% w eb$EoLkop>]_g% w >qppkjiu>qppkj9jas>qppkj$%7 bkni-*?kjpnkho*=``$iu>qppkj%7 iu>qppkj*Patp9?he_gdana7 y ahoa w H]^ahh]^ah9jasH]^ah$%7 bkni-*?kjpnkho*=``$h]^ah%7 y y The intent of this code is obvious. The user has a >qppkj to invoke some action, and, when the action is completed, the button is replaced by a H]^ah control. You might argue that this is not good practice, but let’s examine it for the moment. View state tries to recognize the elements solely by their index. In this example the control at index [0] is the >qppkj. After postback it has been replaced by the H]^ah, using the same index [0]. This does not lead to the exception previously shown, because the restoring code is fairly stupid. It just looks for the right property, and both >qppkj and H]^ah share the same Patp property. While this works, the label now shows the text “Click here”. 7HY4HEEXCEPTIONAPPEARSEVENIFTHISLESS THAN IDEALMETHODFAILS PROBABLYBECAUSETHECONTROL does not provide the expected property. The first solution is simple. Turn off view state for the button control. Since you throw away the button anyway, view state is not required at all. If the view state is being saved, it can’t disturb the NEXTCYCLEPROBLEMSOLVED(EREISTHESOLUTIONINALLITSGLORY lnkpa_pa`rke`L]ca[Ejep$k^fa_poaj`an(Arajp=ncoa% w
C H A P T E R 1 N U N D E R S T A N D I N G A S P . N E T
eb$EoLkop>]_g% w >qppkjiu>qppkj9jas>qppkj$%7 iu>qppkj*Aj]^haReasOp]pa9b]hoa7 bkni-*?kjpnkho*=``$iu>qppkj%7 iu>qppkj*Patp9?he_gdana7 y ahoa w H]^ahh]^ah9jasH]^ah$%7 bkni-*?kjpnkho*=``$h]^ah%7 y y (ADYOUBEENUSINGVIEWSTATEINTHEPRECEDINGSAMPLE THEPROBLEMWOULDRESOLVEITSELFTHAT is, if you had recreated the control after postback, which is one of the basic rules of dynamic con trols creation. The ultimate solution looks like this: lnkpa_pa`rke`L]ca[Ejep$k^fa_poaj`an(Arajp=ncoa% w >qppkjiu>qppkj9jas>qppkj$%7 iu>qppkj*Aj]^haReasOp]pa9b]hoa7 bkni-*?kjpnkho*=``$iu>qppkj%7 iu>qppkj*Patp9?he_gdana7 eb$EoLkop>]_g% w H]^ahh]^ah9jasH]^ah$%7 bkni-*?kjpnkho*=``$h]^ah%7 iu>qppkj*Reoe^ha9b]hoa7 y y (EREYOUARECLOSETOTHEBESTPRACTICE!LLOWALLCONTROLSONTHEPAGETOREMAINUNTOUCHEDAND switch the visibility on and off, as required, via code. The render method is smart enough to not ren der invisible controls. But that’s another topic and would lead us away from view state.
Initializing Dynamically Created Controls I discussed dynamic controls before and, reading the text, you might feel that there are some issues with them. This is indeed something you need to approach carefully, but there are no real issues. The problem is very similar to the one described before. Because you create the control when you choose, you have more influence and this makes your life easier. It does, however, run against the paradigm that says that you should code in a declarative way whenever possible, because you leave the world of declarative definitions completely. In any case, let’s look at how to handle another view state issue correctly. lq^he__h]ooIu?qopki?kjpnkh6?kjpnkh w lnkpa_pa`kranne`arke`?na]pa?deh`?kjpnkho$% w H]^ahh9jasH]^ah$%7 ?kjpnkho*=``$h%7 h*Patp9@]paPeia*Jks*PkHkjc@]pa$%7 y y
51
52
C H APT ER 1 N UNDER S TA NDING A S P . NET
You can create child controls any time, but the ?na]pa?deh`?kjpnkho method is the best opportunity for fitting into the event sequence. The initialize event is called and the control doesn’t miss the tracking of view state. The secret is the behavior of the ?kjpnkho*=`` call. This isn’t just ACOLLECTIONITDOESMUCHMOREWHENTHE=`` method is invoked. Even if all events of the parent hierarchy are complete, the control begins its regular life cycle and all events involved here are fired correctly. That means that the control starts tracking view state immediately after it’s added to the collection. ?na]pa?deh`?kjpnkho always seems too late, as even though it is called at different points in time, it’s always later than KjEjep*USTFORCOMPLETENESS YOUNEEDTOKNOWTHATITSBASED on Ajoqna?deh`?kjpnkho call, which happens in KjLnaNaj`an at the latest. For some reasons the call might come much earlier. It depends on the control’s parts being rendered due to specific condi TIONS4HISHAPPENSVIRTUALLYINDATA BOUNDCONTROLSTHATCANSHOWDIFFERENTTEMPLATESDEPENDING on actual state. Now take a closer look at the solution: lq^he__h]ooIu?qopki?kjpnkh6?kjpnkh w lnkpa_pa`kranne`arke`?na]pa?deh`?kjpnkho$% w H]^ahh9jasH]^ah$%7 h*Patp9@]paPeia*Jks*PkHkjc@]pa$%7 ?kjpnkho*=``$h%7 y y There is only a subtle difference. The control’s properties are set before the control is added, which means that it’s initialized before the KjEjep is fired, and that subsequently it has not yet begun to track the view state. You can also databind controls before they have been added to the parent control’s control hierarchy. This is very powerful and flexible. So real developers write their own custom controls and know how they work internally.
Summary In this chapter you learned about the internal processing of ASP.NET, especially the processing pipeline, which is running a single request and performs the steps required to create the content sent to the client finally. The pipeline forms the life cycle of application, pages, and controls. Dur ing the life cycle several states store current processing step’s data. To rescue data from one request to another, the view state is used. You learned what the view state is for and how to overcome the quirks and traps the implementation has. (OWEVER 7EBAPPLICATIONSHANDLEUSUALLYMORETHANONEREQUESTATATIME4HENEXTCHAPTER extends the description by looking into threading, thread pools, and other stuff required to make an ASP.NET project fast and reliable even if it comes under pressure from multiple requests.
C HAPTER
2
Worker and Threads
I
n the first chapter I looked into the handling of a page request, and, in particular, the application life cycle and page life cycle. This is the fundamental process that runs when anyone requests a page resource. However, Chapter 1 was a simplification of the situation—I assumed that only one request comes in at a time. Reality is quite different. Managing hundreds, or even thousands, of simultaneous requests requires advanced knowledge and skills. In this and subsequent chapters you will learn those skills. This chapter includes:
s (OW!30.%4HANDLESMULTIPLEREQUESTS
s (OWTOMANAGETHEWORKERPROCESSESANDOPTIMIZETHEWORKLOAD
s 4HETHREADMODEL THREADPOOLBEHAVIORANDHOWTOOPTIMIZEITSUSAGE
s (OWTOBUILDASYNCHRONOUS handlers, pages, and tasks
Managing the Worker Process In Chapter 1, you learned about the worker process, w3wp.exe, which executes a request and hosts THE!30.%4ENGINE-ANAGINGTHEWORKERPROCESSISTHEKEYTOMANAGINGHIGHSERVERDEMAND AND keeping your applications stable and reliable.
Managing Worker Processes and AppDomains in IIS7 One of the leading Windows application management tools is Windows Management Instrumentation (WMI)9OUHAVEPROBABLYUSED7-)TOADMINISTERBASIC!30.%4 RELATEDPARTSOF))3)TS a well-known way to access worker processes while your application is running. 4OACCOMMODATETODAYSMANAGED CODEWORLD -ICROSOFTEXPOSEDTHE7-)AND#/-METHOD CALLSVIA.%4)NPREVIOUSPROGRAMMINGENVIRONMENTS THEACCESSTOPROPERTIESANDEVENTSOFTHE MODELSCOMPONENTSISBASEDONUNTYPEDSTRINGS LIKEcapLnklanpu$J]ia%. Instead of invoking 7-)DIRECTLY YOUCANMANAGE))3USING.%4TYPEDCLASSES RATHERTHANUSINGTHOSEUNTYPEDSTRINGS 4HESECLASSESEXPOSEADIRECTINTERFACETOTHE))3MANAGEMENTLEVELANDAPPEARTOBEEASY to use and robust.
53
54
C H APT ER 2 N WO R K ER A ND THR EA DS
GOOD OLD DAYS: WINDOWS MANAGEMENT INSTRUMENTATION Windows exposes the Windows Driver Model as a framework for device drivers. Windows Management Instrumentation (WMI) is a set of extensions that provides information about and notifications from instrumented components. Programming environments and tools use WMI to access the underlying hardware components using the driver model. You can find a good introduction in Wikipedia at: dppl6++aj*segela`e]*knc+sege+Sej`kso[I]j]caiajp[ Ejopnqiajp]pekj. It suggests that WMI is a good tool for automating the management of worker processes and application domains in IIS7. It can also help us find a better way to customize the ASP.NET environment. IIS7 worker processes are spawned by the Windows Process Activation Service (WAS), using w3wp.exe. A worker process can contain AppDomains that are typically created to handle a request.
Prerequisites There areSEVERALPREREQUISITESTOWRITINGSOPHISTICATEDMANAGEMENTAPPLICATIONSUSING6ISUAL3TUDIO&IRSTOFALL ))3ITSELFISAPREREQUISITE.OOTHER7EB3ERVERCURRENTLYSUPPORTSTHESEINTERFACES To test your application, it must run with elevated privileges. If you run the development environMENTON7INDOWS6ISTA YOUMUSTLAUNCH6ISUAL3TUDIOWITH!DMINISTRATORRIGHTS!NAPPLICATION BUILTONSUCHASYSTEMRUNSWELLON7INDOWS3ERVER ASLONGASITRUNSWITH!DMINISTRATOR PRIVILEGES/NCE6ISUAL3TUDIOISRUNNINGWITHTHERIGHTACCOUNT YOUNEEDTOREFERENCETHEADMINISTRATIONASSEMBLIESINYOURPROJECT4HEREARESEVERALMANAGEDASSEMBLIESAVAILABLEINTHE))3 installation folder: 8!Sej@en!:Xouopai/.Xejaponr You will find several assemblies starting with the name “Microsoft.Web.” Depending on what YOUREPLANNINGTODO YOULLNEEDONEORANOTHER&ORTHEMOMENT JUSTREFERENCEALLOFTHEMTO ENSURETHATALLTHEEXAMPLESINTHISCHAPTERRUNPROPERLY(ERESTHELISTOFWHATYOULLNEED
s Ie_nkokbp*Sa^*=`iejeopn]pekj
s Ie_nkokbp*Sa^*I]j]caiajp*=oljap
s Ie_nkokbp*Sa^*I]j]caiajp*=oljap?heajp
s Ie_nkokbp*Sa^*I]j]caiajp
s Ie_nkokbp*Sa^*I]j]caiajp*Eeo
s Ie_nkokbp*Sa^*I]j]caiajp*Eeo?heajp
Tasks to Manage There are several tasks you can manage using the techniques described in this chapter. They will ALLOWYOUTOEXTENDTHEAVAILABLETOOLKITANDCUSTOMIZETHEMANAGEMENTOFTHE!30.%4ENVIRONment. I will focus here on some of the more common tasks, like:
s 6IEWTHECURRENTLYEXECUTINGREQUESTSFORAWORKERPROCESS
s 'ETTHESTATEOFALLWORKERPROCESSES
s 5NLOADASPECIFIC!PP$OMAIN ORALL!PP$OMAINS
s $ISPLAYALL!PP$OMAINSANDTHEIRPROPERTIES
C H A P T E R 2 N W O R K E R A N D T H R E A D S
There is almost unlimited scope for using the new management classes to extend the existing !30.%4MANAGEMENTCAPABILITIES4HISINCLUDES BUTISNOTLIMITEDTO THEFOLLOWING
s .OTIFYWHENANUNHANDLEDEXCEPTIONOCCURS
s 7ATCHLONG LASTINGREQUESTS
s 7ATCHTHENUMBEROFWORKERTHREADSANDSTATUSOFTHREADPOOL
s 0ROVIDETHENUMBEROFUSERSTHATMATCHSPECIFICCONDITIONS
s -ANAGEINTERNALSETTINGSOF!30.%4AND))3
Back Up Your Configuration It is possibleTHATTHEFOLLOWINGSAMPLESWILLDESTROYORDAMAGEYOUR))3CONFIGURATION!SITSCUMbersome to re-install your development environment after any undesirable side effects, you should TESTINAVIRTUALMACHINEORBACKUPTHE))3CONFIGURATIONOFYOURPRODUCTIONMACHINEBEFOREYOU START4HE!PP#MDTOOLISVERYUSEFULTOMAKEINSTANTBACKUPSOFTHECONFIGURATION)THASSEVERAL options. The basic structure of the command is: =ll?i`?kii]j`K^fa_pPulaE@L]n]iapan The _kii]j` parameter depends on the ObjectType parameter. For backup purposes the K^fa_pPula is >]_gql. You can use the following commands:
s LIST: shows all available backups
s ADD: creates a new backup for the current configuration
s DELETE: delete a backup from disk
s RESTORE: restore a backup and overwrite current configuration
4HEUSAGEISSTRAIGHTFORWARD!GAIN REMEMBERTORUNTHISTOOLWITH!DMINISTRATORPRIVILEGESTO ACCESSTHE))3SETTINGS To make a backup of the configuration: 1. /PENACOMMANDWINDOWWITH!DMINISTRATORRIGHTS 2. .AVIGATETO7IN$IR<SYSTEM
Server Too Busy You might beWONDERINGWHENANDWHYTHEh3ERVER4OO"USYvSTATUSISSENTTOTHECLIENT4HISIS (440STATUSERRORANDAPPEARSAMONGOTHERREASONSWHENTHELIMITOFCONCURRENTREQUESTS EXCEEDSTHEDEFAULTVALUEOF WHICHCOULDBECHANGESBYTHEnamqaopMqaqaHeiep parameter MENTIONEDBEFORE4HEACTUALVALUEISEXPOSEDBYTHEh!30.%42EQUESTS#URRENTvPERFORMANCE counter. 3EETHESECTIONh)NSTALLA0ERFORMANCE#OUNTERvFORDETAILSONHOWTOMONITORANDLOGSUCHVALues and discover what is happening to your server at any given moment.
Tune the Threading The previous explanations might discourage you from changing theSETTINGS(OWEVER THEYRE worth exploring when performance issues arise or strange errors occur. This section shows some solutions for the following error messages:
s !PROCESSSERVINGAPPLICATIONPOOLhNAMEvEXCEEDSTIMELIMITSDURINGSHUTDOWN
s 3YSTEM)NVALID/PERATION%XCEPTION4HEREARENOTENOUGHFREETHREADSINTHE4HREAD0OOL object to complete the operation.
s (TTP%XCEPTIONX 2EQUESTTIMEDOUT To solve the preceding problems, try the following:
s ,IMITTHENUMBEROFREQUESTSTHATCANEXECUTEATTHESAMETIMETOAPPROXIMATELYPER #054HISLIMITWORKSWELLFORMOSTAPPLICATIONS
s 0ERMIT7EB3ERVICECALLBACKSTOFREELYUSETHREADSINTHE4HREAD0OOL
s 3ELECTANAPPROPRIATEVALUEFORTHEi]t_kjja_pekjoATTRIBUTE"ASEYOURSELECTIONONTHE NUMBEROF)0ADDRESSESAND!PP$OMAINSTHAT are used (more details follow in the section h3ETMAXCONNECTIONv
Tuning Task by Task This section gives some examples of how and when to change the default settings of the following values:
s MAX7ORKER4HREADS
s MIN7ORKER4HREADS
s MAX)O4HREADS
s MIN&REE4HREADS
s MIN,OCAL2EQUEST&REE4HREADS
s MAXCONNECTION
s EXECUTION4IMEOUT
73
74
C H APT ER 2 N WO R K ER A ND THR EA DS
Set maxWorkerThreads and maxIoThreads !30.%4USES the following two configuration settings to limit the number of worker threads and completion threads used: 8lnk_aooIk`ahi]tSknganPdna]`o9.,i]tEkPdna]`o9.,: The i]tSknganPdna]`o attribute and the i]tEkPdna]`o attribute are implicitly multiplied by THENUMBEROF#05S)NTHEEXAMPLE THEMAXIMUMNUMBEROFWORKERTHREADSISIFYOUHAVE a dual-core processor.
Set minFreeThreads and minLocalRequestFreeThreads !30.%4ALSO contains the following configuration settings, which determine how many worker threads and completion port threads must be available to start a remote request or a local request: 8dpplNqjpeiaiejBnaaPdna]`o94iejHk_]hNamqaopBnaaPdna]`o94: If there are insufficient threads available, the request is queued until sufficient threads are FREETOHANDLETHEREQUEST4HEREFORE !30.%4WILLNOTEXECUTEMORETHANTHEFOLLOWINGNUMBEROF requests at the same time: $i]tSknganPdna]`o&jqi?LQo%)iejBnaaPdna]`o The iejBnaaPdna]`o parameter and the iejHk_]hNamqaopBnaaPdna]`o attributes are not implicITLYMULTIPLIEDBYTHENUMBEROF#05S!SSUMINGYOUHAVEFOUR#05S THEFORMULAGIVENEQUALS PARALLELREQUESTS n
Set minWorkerThreads !30.%4ALSO contains a configuration setting for the number of worker threads to be made available immediately to service a remote request. 8lnk_aooIk`ahiejSknganPdna]`o9-: The specified number of threads that are controlled by this setting can be created at a much FASTERRATETHANWORKERTHREADSTHATARECREATEDFROMTHE#,2SDEFAULTCAPABILITIES4HESEARESTILL regular threads, but they are prepared and ready to use anytime. Requests may suddenly fill the request queue due to a slow-down on a back-end server, a sudden burst of requests from the client end, or something similar that would cause a sudden rise in the number of requests in the queue. The default value for the iejSknganPdna]`o parameter is 1. Microsoft recommends that you set the value for the iejSknganPdna]`o parameter to half of the i]tSknganPdna]`oVALUE"YDEFAULT THE iejSknganPdna]`o parameter is implicitly multiplied byTHENUMBEROF#05S
Set maxconnection The i]t_kjja_pekj attributeDETERMINESHOWMANYCONNECTIONSCANBEMADETOASPECIFIC)0 address. The setting was mentioned before, but here is a more complex scenario: 8_kjja_pekjI]j]caiajp: 8]``]``naoo9&i]t_kjja_pekj9.: 8]``]``naoo9-,*2*.,1*40i]t_kjja_pekj9.,: 8+_kjja_pekjI]j]caiajp: The i]t_kjja_pekjSETTINGAPPLIESATTHE!PP$OMAINLEVEL#ONSEQUENTLY ONLYTWOCONNECTIONSBYDEFAULT CANBEMADETOASPECIFIC)0ADDRESSFROMEACH!PP$OMAININYOURPROCESS
C H A P T E R 2 N W O R K E R A N D T H R E A D S
)NTHISEXAMPLE ALL)0ADDRESSESACCEPTTWOCONNECTIONS EXCEPTFORTHESPECIFIEDONEAT WHICHACCEPTS
Set executionTimeout !30.%4USES the following configuration setting to limit the request execution time (in seconds): 8dpplNqjpeiaata_qpekjPeiakqp95,+: This refers to the Oanran*O_nelpPeiakqp property exposed by the DpplOanran object. If you increase the value of the ata_qpekjPeiakqp attribute, you may have to also modify the naolkjoa@a]`hk_gEjpanr]h attribute of the 8lnk_aooIk`ah: tag.
Install a Performance Counter To monitor theCURRENTWORKLOAD YOUCANUSEA0ERFORMANCE#OUNTER. Open the Reliability and Performance Monitor TOOL0ERF-ON BYENTERINGPerfMon in the Run window. Install the counter using these steps: 1. #LICKON0ERFORMANCE-ONITORINTHETREEVIEW 2. 2IGHT CLICKANDCHOOSE.EWANDTHEN$ATA#OLLECTOR3ET 3. 'IVETHECOLLECTIONANAPPROPRIATENAMESUCHASh-ONITOR))3v ANDCLICK.EXTSEE &IGURE
Figure 2-6. Install new data collection using the Performance Monitor tool. 4. Choose a directory in which the data is to be saved. The default path is SYSTEMDRIVE 75
76
C H APT ER 2 N WO R K ER A ND THR EA DS
9OUCANNOWOPENYOURCOLLECTIONWITHINTHEOBJECTSTREEVIEWATTHELEFTANDNAVIGATETHE PATHTO2ELIABILITYAND0ERFORMANCE°$ATA#OLLECTOR3ETS°5SER$EFINED°-ONITOR))34HEREIS ADEFAULTENTRYHERENAMEDh3YSTEM-ONITOR,OGv9OUCANMODIFYTHISORADDANEWDATACOLLECTOR,ETSADDANEWONETODEMONSTRATETHEPROCESS2IGHT CLICKONTHELEAFENTRYINTHETREENAMED -ONITOR))3ANDCHOOSE.EW°$ATA#OLLECTOR!WIZARDLAUNCHESANDASKSFORANAMEANDCOLLECTIONTYPE.AMEITh))32EQUESTvANDCHOOSE0ERFORMANCECOUNTERDATACOLLECTORASTHETYPESEE &IGURE
Figure 2-7. Add a new Data Collector. #LICKON.EXTTOADDTHECOUNTER/NTHEWIZARDSNEXTSCREEN CHOOSE!DDANDSEARCHFOR THECOUNTER)NTHELEFT HANDAREAYOULLFINDTHE!VAILABLECOUNTERSGROUP3EARCHFORTHESECTION h!30.%4!PPLICATIONvANDEXPANDIT4HELISTISSORTEDALPHABETICALLY3CROLLDOWNTOh2EQUESTS)N !PPLICATION1UEUEv)NTHEh)NSTANCESOFSELECTEDOBJECTvGROUP YOULLFINDALLAPPLICATIONS!CCEPT THEDEFAULTSELECTION??4OTAL??TOVIEWALLREQUESTS#LICK!DDAND/+4HEMONITORBEGINSTO watch the counter and saves the results in the file chosen after the counter is started. 4OVIEWTHERESULTS OPENTHEPATH2ELIABILITYAND0ERFORMANCE° Reports °5SER$EFINED° -ONITOR))3°))32EQUEST)TMIGHTTAKESOMETIMETOLOADTHERESULTS DEPENDINGONWORKLOADAND settings. There are several ways to modify the counter to bind it to specific events. For example, you could add a scheduler to run the counter at a specific time. For instance, if you experience problems BETWEENANDAMONYOURSERVER ITDOESNTMAKESENSETORUNTHETOOLTHEWHOLEDAYANDCOLLECT an unnecessarily large amount of data.
C H A P T E R 2 N W O R K E R A N D T H R E A D S
9OUCANDEFINESTOPCONDITIONSBASEDONTIMEFRAMEORTHESIZEOFTHELOGFILE9OUCANALSO launch a task when the scheduler stops. Depending on the resources of the server, you could run an APPLICATIONTHATAUTOMATICALLYANALYZESTHECONTENT SENDINGTHELOGFILETOANEMAILADDRESS ORJUST copying it to another location. 4HISSHORTDESCRIPTIONDOESNOTCOVERTHE2ELIABILITYAND0ERFORMANCE-ONITORTOOLINALLITS GLORY)TSJUSTAhTEASERvSOTHATYOUKNOWTHEREAREPOWERFULTOOLSAVAILABLETOMONITORASERVERAND find bottlenecks, failures, and performance loss easily. You can also gather hard data to confirm a problem, find a solution, and validate that your solution fixes the PROBLEM3TARTHERE reading more about the toolREGARDING7INDOWS3ERVEROPERATINGSYSTEM dppl6++pa_djap*ie_nkokbp*_ki+ aj)qo+he^n]nu+__311,4-*]opx.
Threading and Asynchronous Operations For most scenarios, the internal thread handling is well-designed and functional. However, there are situations where you reach the limits of the default settings. In particular, if a site comes under pressure from too many requests, the internal thread pool can run out of threads and the server will NOLONGERRESPONDASEXPECTED)LLDISCUSSSEVERALTECHNIQUESFOROVERCOMINGTHIS)LLCONSIDERHOW THECOMMONLANGUAGERUNTIMETHREADPOOLISUSEDBY!30.%4TOSERVICEREQUESTS ASWELLASLOOKing into the pooling mechanisms used for handlers, modules, and applications. )LLALSOSHOWTHETHREADINGUSAGEINDEPENDENTOFOTHERTECHNIQUESANDTHEASYNCHRONOUSPROcessing of requests using internal features to solve common issues.
Threading in ASP.NET To efficientlySERVICEMULTIPLECLIENTREQUESTS 7EB3ERVERSMAKEEXTENSIVEUSEOFCONCURRENCYBY launching multiple processes and spawning multiple threads to service requests. Considering the CONSTRUCTIONANDBEHAVIOROFTHE!30.%4ENGINE ITSEEMSTHATDEVELOPERSNEEDNOTCONCERNTHEMselves with threading at all, as the challenging aspects are handled internally. This is correct for most scenarios; page requests are serviced on the same thread, and a separate instance is created to service each new request. However, there are scenarios where you reach the limits of this model—as every model has limits—and need to extend the behavior. &IRSTOFALL ACLEARUNDERSTANDINGOFTHEINTERNALBEHAVIORISREQUIRED3OMEPARTSHAVEBEEN explained in the previous sections but I will repeat it from the perspective of threading. The processWIDE#,2THREADPOOLSERVICESREQUESTS4HETHREADPOOLSIZEISSETTOWORKERTHREADSAND)/ threads by default. Recall the 8lnk_aooIk`ah: settings explained already: 8lnk_aooIk`ahaj]^ha9pnqa i]tSknganPdna]`o9.1 i]tEkPdna]`o9.1+: !SEXPLAINEDIN#HAPTER FOREACHINCOMINGREQUESTANINSTANCEOFTHETYPEDppl=llhe_]pekj is CREATED4OAVOIDREALLOCATINGAPPLICATIONSANDMODULES EACH!PP$OMAINHOLDSAPOOLOFAPPLICATIONINSTANCESAND(440MODULES4HESIZEOFTHISPOOLISALSOTHATMEANSTHATREQUESTSCAN be handled per worker process.
77
78
C H APT ER 2 N WO R K ER A ND THR EA DS
The Need for Asynchrony )MAGINETHATYOUNEEDTOEXCLUSIVELYREQUESTARESOURCE!SLONGASONEPERSONISUSINGANAPPLIcation, this is not a problem. However, web applications are typically employed by many, if not thousands, of concurrent users. The thread pool and thread handling design allows several requests TOBEEXECUTEDINPARALLELTOIMPROVETHEUSERSEXPERIENCEWHENUSINGAWEBAPPLICATION,IMITING THENUMBEROFTHREADSISIMPORTANTINORDERTOALLOWMORETHANONEREQUESTPER#05ORCORE#REATing a vast number of threads can cause a system to a grind to a halt. (OWEVER AREQUESTCANLAUNCHDIFFERENTKINDSOFTASKS0ROCESSINGAPAGEANDSENDING RESOURCESTOTHECLIENTISTHEMOSTCOMMONACTION2EQUESTINGDATAFROMADATABASE 233FEED OR 7EB3ERVICEISANOTHER2EQUESTINGDATACANBETIMECONSUMING RATHERTHANPROCESSORINTENSIVE 7HATHAPPENSTHEN4HENUMBEROFTHREADSINTHETHREADPOOLQUICKLYREACHESITSLIMITANDSUBSEQUENTREQUESTSARENOTHANDLEDASEXPECTED$ESPITETHIS THE#05ISONLYIDLING!SYOULEARNEDIN THEPREVIOUSSECTIONS THESETTINGSALLOWYOUTOCHANGETHENUMBEROFCONCURRENTTHREADS"UTTHIS is not a solution either, as your system has to handle both kinds of requests: short-lived requests for resources and long-running queries against other servers. 4OMAKETHINGSCLEARER LOOKAT,ISTING )TDELAYSAREQUESTWHILEDEMANDINGLITTLEFROMTHE #05)TDISPLAYSTHETHREAD)$TOSHOWWHETHERORNOTANEWTHREADISCREATEDTOHANDLETHEREQUEST Listing 2-7. A Simple Page That Slows Down a Request 8!]_gcnkqj`Pdna]`o9pnqa7 lq^he_^kkhQoa>]_gcnkqj`Pdna]`o w capwnapqnjqoa>]_gcnkqj`Pdna]`o7y y lner]pa^kkhlnklkc]paPdna]`Lnej_el]h9b]hoa7 lner]pa^kkhlnklkc]pa?]hh?kjpatp9b]hoa7 lner]pa^kkhlnklkc]paDppl?kjpatp9b]hoa7 lner]pa^kkhlnklkc]pa?=OI]ngano9b]hoa7 y
C H A P T E R 2 N W O R K E R A N D T H R E A D S
"EFORELOOKINGINTOTHEIMPLEMENTATIONDETAILSOFTHEUSAGESCENARIO ITISHELPFULTOUNDERSTAND how it works: ?qopkiPdna]`Lkkhpl-9jas?qopkiPdna]`Lkkh$.(2(Lkkh-%7 pl-*Lneknepu9Ouopai*Pdna]`ejc*Pdna]`Lneknepu*>ahksJkni]h7 pl-*JasPdna]`Pneccan90,,7 pl-*@uj]ie_Pdna]`@a_]u91,,,7 pl-*Op]np$%7 This starts a new thread pool using two initial threads and defines a maximum of six with low priority. The decay time is five seconds for testing purposes—the default is five minutes. The new THREADTRIGGERISSETTOMILLISECONDS The code used to test the thread pool requires an interface and a delegate, as defined in ,ISTING Listing 2-15. Delegates and Base Interface for the Thread Pool lq^he_`ahac]parke`SkngNamqaop@ahac]pa$k^fa_pop]pa(± @]paPeianamqaopAjmqaqaPeia%7 lq^he_`ahac]parke`Pdna]`Lkkh@ahac]pa$%7 nacekjESkngNamqaopejpanb]_a lq^he_ejpanb]_aESkngNamqaop w ^kkh?]j_ah$%7 y aj`nacekj Once the pool is up and running, you can issue requests to get threads out of it. Doing so REQUIRESACALLBACKTHATSRESPONSIBLETOHANDLETHEWORKLOAD SkngNamqaop@ahac]pa_^-9jasSkngNamqaop@ahac]pa$?]hh^]_gKja%7 opnejc]nc97 Then a request internally starts the thread, using the callback to handle the worker method: pl-*LkopNamqaop$_^-(]nc%7 Once the threads are no longer required, the pool can be stopped: pl-*Opkl=j`S]ep$%7 This method call assures that the currently running threads can properly end its work before THETHREADPOOLISGOINGDOWNSEE,ISTING Listing 2-16. The ThreadInfo Class That Provides the Thread-Specific HttpContext ejpanj]h_h]ooPdna]`Ejbk w lq^he_op]pe_Pdna]`Ejbk?]lpqna$^kkhlnklkc]paPdna]`Lnej_el]h(± ^kkhlnklkc]pa?]hh?kjpatp(± ^kkhlnklkc]paDppl?kjpatp(± ^kkhlnklkc]pa?=OI]ngano% w napqnjjasPdna]`Ejbk$lnklkc]paPdna]`Lnej_el]h(lnklkc]pa?]hh?kjpatp(± lnklkc]paDppl?kjpatp(lnklkc]pa?=OI]ngano%7 y lq^he_op]pe_Pdna]`EjbkEilanokj]pa$Pdna]`Ejbkpe%
97
98
C H APT ER 2 N WO R K ER A ND THR EA DS
w eb$pe99jqhh%pdnksjas=ncqiajpJqhhAt_alpekj$pe%7 Pdna]`EjbklnarEjbk9?]lpqna$pnqa(pnqa(pnqa(pnqa%7 Naopkna$pe%7 napqnj$lnarEjbk%7 y lq^he_op]pe_rke`Naopkna$Pdna]`Ejbkpe% w eb$pe99jqhh%pdnksjas=ncqiajpJqhhAt_alpekj$pe%7 ++Naopkna_]hh_kjpatp* eb$ieOapHkce_]h?]hh?kjpatp9jqhh% w ieOapHkce_]h?]hh?kjpatp*Ejrkga$Pdna]`*?qnnajpPdna]`(± jask^fa_pWYwpe*_]hh?kjpatpy%7 y ++NaopknaDppl?kjpatpsepdpdaikn]hamqer]hajpkb ++Dppl?kjpatp*?qnnajp9pe*dppl?kjpatp7 ?]hh?kjpatp*Oap@]p]$Dppl?kjpatpOhkpJ]ia(pe*dppl?kjpatp%7 ++Naopknapdna]`e`ajpepu*Ep#oeilknp]jppd]ppdeo^a`kja]bpan ++naopknejc_]hh_kjpatp]^kra(oej_anaopknejc_]hh_kjpatp]hok ++kransnepaopda_qnnajppdna]`lnej_el]hoappejc*Eb ++lnklkc]pa?]hh?kjpatp ++]j`lnklkc]paPdna]`Lnej_el]h]na^kpdpnqa(pdajpda ++bkhhksejceona`qj`]jp* ++Dksaran(oej_alnklkc]pejc_]hh_kjpatpnamqenaopdaqoa ++kbnabha_pekjpk_]lpqna+naopkna_]hh_kjpatp(Es]jp ++pd]p^ad]reknpk^aej`alaj`ajphu ++osep_d]^haokpd]pep_]j^a`eo]^ha`7sdehaopehh]hhksejc ++pdna]`lnej_el]hpk^alnklkc]pa`* ++Pdeo]hok_kranoqoejpdaarajppd]p_]hh_kjpatp ++lnkl]c]pekj_d]jcaookpd]pepjkhkjcanlnklkc]paopdna]`lnej_el]h* Pdna]`*?qnnajpLnej_el]h9pe*lnej_el]h7 y lner]paPdna]`Ejbk$^kkhlnklkc]paPdna]`Lnej_el]h(^kkhlnklkc]pa?]hh?kjpatp(± ^kkhlnklkc]paDppl?kjpatp(^kkhlnklkc]pa?=OI]ngano% w eb$lnklkc]paPdna]`Lnej_el]h% w lnej_el]h9Pdna]`*?qnnajpLnej_el]h7 y eb$lnklkc]paDppl?kjpatp% w dppl?kjpatp9Dppl?kjpatp*?qnnajp7 y eb$lnklkc]pa?]hh?kjpatp""$ieCapHkce_]h?]hh?kjpatp9jqhh%% w _]hh?kjpatp9$Hkce_]h?]hh?kjpatp%± ieCapHkce_]h?]hh?kjpatp*Ejrkga$Pdna]`*?qnnajpPdna]`(jqhh%7 _]hh?kjpatp9$Hkce_]h?]hh?kjpatp%_]hh?kjpatp*?hkja$%7
C H A P T E R 2 N W O R K E R A N D T H R E A D S
y y ELnej_el]hlnej_el]h7 Hkce_]h?]hh?kjpatp_]hh?kjpatp7 ++=hs]uojqhhqjpehCap+Oap?kilnaooa`Op]_g]naklaja`ql* ?kilnaooa`Op]_g_kilnaooa`Op]_g9jqhh7 Dppl?kjpatpdppl?kjpatp7 ++?]_da`pulaejbkni]pekj* _kjop>ej`ejcBh]co^bJkjLq^he_Ejop]j_a9>ej`ejcBh]co*Ejop]j_ax± >ej`ejcBh]co*JkjLq^he_7 _kjop>ej`ejcBh]co^bJkjLq^he_Op]pe_9>ej`ejcBh]co*Op]pe_x± >ej`ejcBh]co*JkjLq^he_7 op]pe_Iapdk`EjbkieCapHkce_]h?]hh?kjpatp9± pulakb$Pdna]`%*CapIapdk`$CapHkce_]h?]hh?kjpatp(^bJkjLq^he_Ejop]j_a%7 op]pe_Iapdk`EjbkieOapHkce_]h?]hh?kjpatp9± pulakb$Pdna]`%*CapIapdk`$OapHkce_]h?]hh?kjpatp(^bJkjLq^he_Ejop]j_a%7 op]pe_opnejcDppl?kjpatpOhkpJ]ia7 op]pe_Pdna]`Ejbk$% w ++Hkkgqlpdar]hqakbDppl?kjpatp*?]hh?kjpatpOhkpJ]ia$ebepateopo% ++pkoaasd]ppdaj]iakbpda_]hh_kjpatpohkpeo ++sdanaDppl?kjpatp*?qnnajp ++eoop]oda`*=o]b]hh^]_g(ebpdeobeah`eoj#plnaoajp]juikna(fqop ++pnubknpdaknecej]hDppl?kjpatpohkpj]ia* Beah`Ejbkbe9pulakb$Dppl?kjpatp%*CapBeah`$?]hh?kjpatpOhkpJ]ia(± ^bJkjLq^he_Op]pe_%7 eb$be9jqhh% w Dppl?kjpatpOhkpJ]ia9$opnejc%be*CapR]hqa$jqhh%7 y ahoa w Dppl?kjpatpOhkpJ]ia9Dppl?kjpatp7 y y y The Pdna]`Ejbk class handles logical information regarding the current thread. This includes the Dppl?kjpatp object of current request. The idea is that several parallel running threads handle different contexts and need to restore the one currently being executed. The data are held in the Hkce_]h?]hh?kjpatpCLASSTHATHOLDSASETOFSERIALIZABLEPROPERTIESSEE,ISTING Listing 2-17. The ThreadWrapper Class That Wraps the Actual Thread ejpanj]h_h]ooPdna]`Sn]llan w lner]pa?qopkiPdna]`Lkkhlkkh7 lner]pa^kkheoLani]jajp7 lner]paPdna]`Lneknepulneknepu7 lner]paopnejcj]ia7
99
100
C H APT ER 2 N WO R K ER A ND THR EA DS
lq^he_Pdna]`Sn]llan$?qopkiPdna]`Lkkhlkkh(^kkheoLani]jajp(± Pdna]`Lneknepulneknepu(opnejcj]ia% w pdeo*lkkh9lkkh7 pdeo*eoLani]jajp9eoLani]jajp7 pdeo*lneknepu9lneknepu7 pdeo*j]ia9j]ia7 hk_g$lkkh% w ++Ql`]papdapkp]hkbpdna]`oejpdalkkh* lkkh*?qnnajpPdna]`?kqjp''7 y y lq^he_rke`Op]np$% w Pdna]`p9jasPdna]`$jasPdna]`Op]np$Pdna]`Lnk_%%7 p*=l]npiajpOp]pa9=l]npiajpOp]pa*IP=7 p*J]ia9j]ia7 p*Lneknepu9lneknepu7 p*Eo>]_gcnkqj`9lkkh*Qoa>]_gcnkqj`Pdna]`o7 p*Op]np$%7 y rke`Pdna]`Lnk_$% w ^kkh`kja9b]hoa7 sdeha$`kja% w SkngNamqaopsn9jqhh7 Pdna]`Sn]llanjasPdna]`9jqhh7 hk_g$lkkh% w ++=ohkjc]opdanamqaopmqaqaeoailpu]j`]odqp`ksjd]oj#p ++^aajejepe]pa`(s]epbkn]jasskngnamqaoppk]nnera* ^kkhpeia`Kqp9b]hoa7 sdeha$lkkh*OpklEjLnkcnaoo""peia`Kqp""± $lkkh*NamqaopMqaqa*?kqjp99,%% w eb$Ikjepkn*S]ep$lkkh(± $eoLani]jajp;Peiakqp*Ejbejepa6lkkh*@a_]uPeia%%% w ++Peia`kqps]epejcbknokiapdejcpk`k* ++Kjhu`uj]ie_]hhu_na]pa` ++pdna]`osehhcapdana(ok^]ehkqp* peia`Kqp9pnqa7 y y ++Saatepa`pdahkkl]^kra^a_]qoakjakbpda ++bkhhksejc_kj`epekjos]oiap6 ++)Pdna]`Lkkh*Opkls]o_]hha`pkejepe]pa]odqp`ksj* ++)=`uj]ie_pdna]`peia`kqps]epejcbkn]skngnamqaop ++)Pdana]naepaioejpdaskngmqaqapklnk_aoo* ++Ebsaatepa`pdahkkl^a_]qoapdana#oskngpk^a`kja(]
C H A P T E R 2 N W O R K E R A N D T H R E A D S
++odqp`ksjd]oj#p^aajejepe]pa`(]j`epeoj#p]`uj]ie_pdna]` ++pd]ppeia`kqp(lqhhpdanamqaopkbbpdamqaqa]j`lnal]napk ++lnk_aooep* eb$lkkh*OpklEjLnkcnaoo""peia`Kqp""± $lkkh*NamqaopMqaqa*?kqjp:,%% w sn9$SkngNamqaop%lkkh*NamqaopMqaqa*@amqaqa$%7 @a^qc*=ooanp$sn9jqhh%7 ++?da_gpkoaaebpdeoskngnamqaoph]jcqeoda`ejpdamqaqa ++ranuhkjc*Ebeps]oejpdamqaqa:9pdajaspdna]` ++pneccanpeia(]j`ebsad]raj#pna]_da`pdai]tpdna]` ++_kqjp_]l(]``]jaspdna]`pkpdalkkh* ++Ebpda`a_eoekjeoi]`a(_na]papdajaspdna]`k^fa_p ++$ql`]pejcpda_qnnajpkbpdna]`oejpdalkkh%( ++^qp`abanop]npejcpdajas ++pdna]`qjpehpdahk_geonaha]oa`* PeiaOl]jnamqaopPeiaEjM9@]paPeia*Jks*Oq^pn]_p$sn*skngejcPeia%7 eb$$namqaopPeiaEjM:9lkkh*JasPdna]`PneccanPeiaOl]j%""± $lkkh*?qnnajpPdna]`?kqjp8lkkh*I]tPdna]`?kqjp%% w jasPdna]`9± jasPdna]`Sn]llan$lkkh(b]hoa(lneknepu(± opnejc*Bkni]p$w,y$`uj]ie_%(± lkkh*Pdna]`LkkhJ]ia%%7 ++Oej_apda_qnnajpnamqaopsafqop`amqaqa`eoop]ha( ++aranupdejcahoa^adej`epejpdamqaqaeo]hokop]ha* ++Oknaoappdapeiaop]ilokbpdanai]ejejclaj`ejcskng ++namqaopookpd]psa`kj#pop]np_na]pejcpdna]`o ++bknaranuoq^oamqajpnamqaop* lkkh*NaoapSkngNamqaopPeiao$%7 y y ahoa w ++Odkqh`kjhucapdanaebpdeoeo]`uj]ie_pdna]`pd]p ++peia`kqps]epejcbkn]skngnamqaop(knebpdalkkh ++eoodqppejc`ksj* lkkh*?qnnajpPdna]`?kqjp))7 eb$lkkh*?qnnajpPdna]`?kqjp99,% w ++H]opkjakqppqnjokbbpdahecdpo* lkkh*KjOpklla`$%7 lkkh*Opkl?kilhapaArajp*Oap$%7 y `kja9pnqa7 y y++hk_g ++Jkhkjcandkh`ejclkkhhk_gdana*** eb$`kja""$sn9jqhh%% w
101
102
C H APT ER 2 N WO R K ER A ND THR EA DS
++?da_gpkoaaebpdeonamqaopd]o^aaj_]j_ahha`sdeha ++opq_gejpdaskngmqaqa* ++Ebpdaskngnamqaops]olaj`ejc(i]ngeplnk_aooa`]j` ++lnk_aa`pkd]j`ha*Kpdanseoa(pdanamqaopiqopd]ra^aaj ++_]j_ahha`^abknasalhq_ga`epkbbpdanamqaopmqaqa* eb$Ejpanhk_ga`*?kil]naAt_d]jca$nabsn*op]pa(± SkngNamqaop*LNK?AOOA@(± SkngNamqaop*LAJ@EJC%9SkngNamqaop*LAJ@EJC% w ++Namqaops]o_]j_ahha`^abknasa_kqh`capdana* ++>]ehkqp* _kjpejqa7 y eb$jasPdna]`9jqhh% w jasPdna]`*Op]np$%7 y ++@eol]p_dpdaskngnamqaop* Pdna]`Ejbkknecej]hPdna]`Ejbk9jqhh7 pnu w ++Eilanokj]pa$]oiq_d]olkooe^ha%sd]psagjks]^kqp ++pdapdna]`pd]peooqa`pdaskngnamqaop* knecej]hPdna]`Ejbk9Pdna]`Ejbk*Eilanokj]pa$sn*pdna]`Ejbk%7 SkngNamqaop@ahac]pap]ncapLnk_9sn*p]ncapLnk_]o± SkngNamqaop@ahac]pa7 eb$p]ncapLnk_9jqhh% w p]ncapLnk_$sn*lnk_=nc(sn*peiaOp]ilOp]npa`%7 y ahoa w sn*p]ncapLnk_*@uj]ie_Ejrkga$sn*lnk_=nco%7 y y _]p_d$At_alpekja% w y bej]hhu w ++Naopknakqnsknganpdna]`#oe`ajpepu* ++ Pdna]`Ejbk*Naopkna$knecej]hPdna]`Ejbk%7 y y y y y 7HILETHETHREADISTHERETODOTHEWORK ANOTHERPIECEINTHEPUZZLEISREQUIREDTHESkngNamqaop. This type contains the reference between the callback method that is handling the workload and the THREAD,ISTING
C H A P T E R 2 N W O R K E R A N D T H R E A D S
Listing 2-18. The WorkRequest Class That Associates Request and Thread ejpanj]h_h]ooSkngNamqaop6ESkngNamqaop w ejpanj]h_kjopejpLAJ@EJC9,7 ejpanj]h_kjopejpLNK?AOOA@9-7 ejpanj]h_kjopejp?=J?AHHA@9.7 lq^he_SkngNamqaop$SkngNamqaop@ahac]pa_^(k^fa_p]nc( ^kkhlnklkc]paPdna]`Lnej_el]h(^kkhlnklkc]pa?]hh?kjpatp( ^kkhlnklkc]paDppl?kjpatp(^kkhlnklkc]pa?=OI]ngano% w p]ncapLnk_9_^7 lnk_=nc9]nc7 lnk_=nco9jqhh7 Ejepe]heva$lnklkc]paPdna]`Lnej_el]h(lnklkc]pa?]hh?kjpatp( lnklkc]paDppl?kjpatp(lnklkc]pa?=OI]ngano%7 y lq^he_SkngNamqaop$@ahac]pa_^(k^fa_pWY]nco( ^kkhlnklkc]paPdna]`Lnej_el]h(^kkhlnklkc]pa?]hh?kjpatp( ^kkhlnklkc]paDppl?kjpatp(^kkhlnklkc]pa?=OI]ngano% w p]ncapLnk_9_^7 lnk_=nc9jqhh7 lnk_=nco9]nco7 Ejepe]heva$lnklkc]paPdna]`Lnej_el]h(lnklkc]pa?]hh?kjpatp( lnklkc]paDppl?kjpatp(lnklkc]pa?=OI]ngano%7 y rke`Ejepe]heva$^kkhlnklkc]paPdna]`Lnej_el]h(^kkhlnklkc]pa?]hh?kjpatp( ^kkhlnklkc]paDppl?kjpatp(^kkhlnklkc]pa?=OI]ngano% w skngejcPeia9peiaOp]ilOp]npa`9@]paPeia*Jks7 pdna]`Ejbk9Pdna]`Ejbk*?]lpqna$lnklkc]paPdna]`Lnej_el]h(± lnklkc]pa?]hh?kjpatp(± lnklkc]paDppl?kjpatp(± lnklkc]pa?=OI]ngano%7 y lq^he_^kkh?]j_ah$% w ++Ebpdaskngnamqaops]olaj`ejc(i]ngep_]j_ahha`*Kpdanseoa( ++pdeoiapdk`s]o_]hha`pkkh]pa*Jkpapd]ppdeo_]hh_]j ++_]j_ah]jklan]pekjsepdkqp]jun]_a_kj`epekjo*>qpebpda ++naoqhpkbpdeopaop)]j`)oapej`e_]paopdanamqaopeoejpda ++lnk_aooa`op]pa(epiecdp]_pq]hhu^a]^kqppk^alnk_aooa`* napqnj$Ejpanhk_ga`*?kil]naAt_d]jca$nabop]pa(?=J?AHHA@(LAJ@EJC%± 99LAJ@EJC%7 y ++Bqj_pekjpk_]hh* ejpanj]h@ahac]pap]ncapLnk_7 ++Op]papkl]oopkbqj_pekj* ejpanj]hk^fa_plnk_=nc7 ++Qoa`sepd@ahac]pa*@uj]ie_Ejrkga*
103
104
C H APT ER 2 N WO R K ER A ND THR EA DS
ejpanj]hk^fa_pWYlnk_=nco7 ++Peiaskngnamqaops]oknecej]hhuajmqaqa`$dah`_kjop]jp%* ejpanj]h@]paPeiapeiaOp]ilOp]npa`7 ++?qnnajppeiaop]ilqoa`bknpneccanejcjaspdna]`o$ikrejcp]ncap%* ejpanj]h@]paPeiaskngejcPeia7 ++Aranupdejcsagjks]^kqp]pdna]`* ejpanj]hPdna]`Ejbkpdna]`Ejbk7 ++Pdaop]pakbpdeol]npe_qh]nnamqaop* ejpanj]hejpop]pa9LAJ@EJC7 y The goal is the function that contains the worker code stored in p]ncapLnk_. The field lnk_=nco held the particular arguments. The field pdna]`Ejbk contains the associated information, especially the Dppl?kjpatp object. This all creates the necessary functions to have threads ready to be used any time and indepenDENTOFTHEINTERNALTHREADPOOL.OWTHATYOUHAVEACUSTOMTHREADPOOLIMPLEMENTATION ITSTIME to use it.
Using the Custom Thread Pool with ASP.NET’s Asynchronous Handler The following example is simply an extended version of the previous one. Instead of using single threads, however, and risk having too many of them, you can use the custom thread pool and set SOMELIMITATIONSSEE,ISTING Listing 2-19. Using the Thread Pool 8))Beha6=ouj_Lkkh*]odt)): 8!acejLnk_aooNamqaop$Dppl?kjpatp_pt(± =ouj_?]hh^]_g_^(k^fa_pk^f% w =ouj_NamqaopOp]panamOp]pa9± jas=ouj_NamqaopOp]pa$_pt(_^(k^f%7 [pdna]`Lkkh*LkopNamqaop$jasSkngNamqaop@ahac]pa$Lnk_aooNamqaop%(namOp]pa%7 napqnjnamOp]pa7 y lq^he_rke`Aj`Lnk_aooNamqaop$E=ouj_Naoqhp]n% w y rke`Lnk_aooNamqaop$k^fa_pop]pa(@]paPeianamqaopPeia% w =ouj_NamqaopOp]panamOp]pa9op]pa]o=ouj_NamqaopOp]pa7 ++P]gaokiapeiapk`kep Pdna]`*Ohaal$.,,,%7 namOp]pa*[_pt*Naolkjoa*Kqplqp*Snepa$± =ouj_Pdna]`Lkkh(w,y(± =ll@ki]ej*Cap?qnnajpPdna]`E`%7 ++pahh]ol*japukq]nabejeoda`lnk_aooejcpdeonamqaop namOp]pa*?kilhapaNamqaop$%7 y y y )NTHEORY THISTHREADPOOLWILLNOTRUNOUTOFTHREADSUNLESSITDOESNOTREACHTHELIMITOF THREADSALLOWED(OWEVER THEREARESOMELIMITATIONS SUCHASAVAILABLEMEMORY #05POWER AND operating system restrictions. This is why the pool was designed, so that you can optionally specify limits. The previous code is a good platform for experimenting with threads and thread pools, and for monitoring the number of threads for your application. If you want to replace the internal thread pool, try out the custom pool toLEARNHOW!30.%4REQUESTSTHREADS, and find out where to improve performance.
Summary This chapter covered in-depth information about the internal request processing and how to tweak the worker process and the threading. You were given an idea of how the thread pool works and how TOEXTENDTHEBEHAVIOR5SINGASYNCHRONOUSPROCESSINGMIGHTINCREASEPERFORMANCEANDHANDLE HIGHWORKLOAD!CUSTOMTHREADPOOLWASINTRODUCEDTODEMONSTRATEHOWTOCHANGEINTERNALPARTS of the processing pipeline transparently. In anticipation of Chapter 3, you had a first look into asynchronous handlers and their usage.
105
C HAPTER
3
Modules and Handlers
I
n this chapter, you will look more closely at the extensibility of ASP.NET through the addition of custom modules and handlers. Handlers and modules are integrated into IIS so that your web applications can perform and scale well. In Chapter 2, you learned how to use asynchronous handlers to handle custom threads. Here, you’ll learn more about how ASP.NET can extend using handlers. This chapter includes:
s 7HATINTERNALMODULESAREFOR
s (OWTOCREATE ACTIVATE ANDDEBUGCUSTOMMODULES
s 4HEHANDLERSINCLUDEDIN!30.%4
s (OWTOEXTEND CUSTOMIZE ANDREPLACEHANDLERS
s 7RITINGYOUROWNHANDLERSWITHBOTHSYNCHRONOUSANDASYNCHRONOUSBEHAVIOR
Module, Handlers, and IIS ))37EB3ERVERFEATURESFITINTOONEOFTWOCATEGORIES
s -ODULES
s (ANDLERS
Similar to the ISAPI filterINPREVIOUS))3VERSIONS AMODULE participates in the processing of each request. Its role is to change or add content to the request. Examples of some out-of-the-box modules in IIS7 include authentication modules, compression modules, and logging modules. The names indicate the function of each module. A module is a .NET class that implements the Ouopai*Sa^*EDpplIk`qha interface and uses APIs in the Ouopai*Sa^ namespace to participate in one or more of ASP.NET’s request-processing stages. I explained the stages of this “pipeline” in Chapter 1. By contrast, a handler, similar to the ISAPI extensionINPREVIOUS))3VERSIONS ISRESPONSIBLEFOR handling requests and creating responses for specific content types. The main difference between modules and handlers is that handlers typically map to a particular request path or extension. They also support the processing of a specific resource to which that path or extension corresponds. (ANDLERSPROVIDEDWITH))3INCLUDE!30.%4SL]caD]j`hanB]_pknu, which processes .aspx pages, among others. This kind of a handler is a .NET class that implements the ASP.NET Ouopai*Sa^* EDpplD]j`han or Ouopai*Sa^*EDppl=ouj_D]j`han interface. It uses APIs in the Ouopai*Sa^ namespace to produce an HTTP response for the specific content it creates. 7HENDEVELOPINGAN))3FEATUREOR!30.%4EXTENSION YOULLNEEDTODECIDEWHETHERAMODule or a handler is appropriate. No common task requires both. If your feature is responsible for SERVINGREQUESTSTOASPECIFIC52,ORFILEEXTENSION LIKE&*ljc, then a handler is the right choice, as 107
108
C H APT ER 3 N MODU L ES A ND HA NDL ER S
HANDLERSAREPRIMARILYFORSPECIFICTASKS!LTERNATIVELY IFYOUWANTTORESPONDTOSOMEORALLREQUESTS a module is appropriate. #REATINGIMAGESONTHEFLYCORRESPONDSTOASPECIFICFILETYPEUSEAHANDLERTOACHIEVETHIS Adding footers to all your pages from one location is a good idea—implement a module to do that.
Modules 4HISSECTIONEXPLAINSHOWTOCREATEINTERNALMODULESANDGIVESSOMEEXAMPLESTHATYOUCANUSEIN your own applications.
IIS7 Architecture ASP.NET isTIGHTLYINTEGRATEDWITH))3%VENTHOUGHITISPOSSIBLE to run ASP.NET with any host, thanks to its modular architecture, you should keep in mind that IIS is the best platform “by design.” %XTENDINGANDCUSTOMIZING!30.%4ISONLYPOSSIBLEWITHAGOODUNDERSTANDINGOF))3ANDITSPARTS -ICROSOFTCHANGEDLARGEPARTSOFTHEARCHITECTUREOF))3COMPAREDTOPREVIOUSVERSIONS/NEOF THEMAJORCHANGESWASTHEGREATLYENHANCEDEXTENSIBILITY)NSTEADOFAPOWERFULYETMONOLITHIC7EB 3ERVER ))3NOWHASA7EB3ERVERENGINETOWHICHYOUCANADDORREMOVECOMPONENTS4HESECOMponents are called modules. -ODULESBUILDTHEFEATURESOFFEREDBYTHE7EB3ERVER!LLMODULESHAVEONEPRIMARYTASK PROCESSINGAREQUEST4HISCANBECOMECOMPLICATED HOWEVER ASAREQUESTISNOTJUSTACALLTOSTATIC RESOURCES#ONSIDERREQUESTSINVOLVINGTHEAUTHENTICATIONOFCLIENTCREDENTIALS COMPRESSIONAND decompression, or cache management. Assuming that IIS7 is the primary platform for running ASP.NET, any discussion about extensibility does not make sense without understanding what accompanies IIS7. IIS7 comes with two module types:
s .ATIVE-ODULES
s -ANAGED-ODULES
Native Modules .ATIVEMODULESPERFORMALLTHEBASICTASKSOFA7EB3ERVER(OWEVER NOTALLMODULESMANAGECOMMONREQUESTS)TDEPENDSONYOURINSTALLATIONANDCONFIGURATIONASTOWHETHERAMODULEISAVAILABLE and running. Inside IIS7 are:
s (440-ODULES
s 3ECURITY-ODULES
s #ONTENT-ODULES
s #OMPRESSION-ODULES
s #ACHING-ODULES
s ,OGGINGAND$IAGNOSING-ODULES
s )NTEGRATIONOF-ANAGED-ODULES
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
You can find all modules—apart from those whose full path follow—in the following directory: !Sej@en!XOuopai/.XEjapOnr As shown in Table 3-1, the HTTP modules perform tasks specific to Hypertext Transfer Protocol. Table 3-1. HTTP Modules Module Name
DLL
Description
?qopkiAnnknIk`qha
Custerr.dll
Its purpose is to send default or configured HTTP error messages when the response contains an error status.
DpplNa`ena_pekjIk`qha
2EDIRECTDLL
-ODULESUPPORTSCONFIGURABLEREDIRECTIONFOR HTTP requests.
Lnkpk_khOqllknpIk`qha
Protsup.dll
This module executes protocol-related actions, such as setting response headers and redirecting headers.
SecurityISESSENTIALFORA7EB3ERVER ASSHOWNBYTHENUMBEROFMODULESIN4ABLE Table 3-2. Security Modules
Module Name
DLL
Description
=jkjuikqo=qpdajpe_]pekjIk`qha
Authanon.dll
Accomplishes anonymous authentication in case other authentication methods fail.
>]oe_=qpdajpe_]pekjIk`qha
Authbas.dll
Performs Basic authentication.
?anpebe_]paI]llejc=qpdajpe_]pekjIk`qha
Authcert.dll
0ERFORMS#ERTIFICATE-APPING AUTHENTICATIONUSING!CTIVE $IRECTORY
@ecaop=qpdajpe_]pekjIk`qha
Authmd5.dll
0ERFORMS$IGESTAUTHENTICATION
EEO?anpebe_]paI]llejc=qpdajpe_]pekjIk`qha
Authmap.dll
0ERFORMS#ERTIFICATE-APPING authentication using IIS certificate configuration.
NamqaopBehpanejcIk`qha
-ODRQFLTDLL
Performs tasks such as conFIGURINGALLOWEDVERBSANDFILE extensions, setting limits, and scanning for bad character seQUENCESBYSCANNINGTHE52,
Qnh=qpdknev]pekjIk`qha
5RLAUTHZDLL
%XECUTES52,AUTHORIZATION
Sej`kso=qpdajpe_]pekjIk`qha
Authsspi.dll
0ERFORMS.4,-INTEGRATED authentication.
ElNaopne_pekjIk`qha
iprestr.dll
4HEPURPOSEISTORESTRICT)0V addresses listed in the IpSecurity list.
/NCETHEREQUESTISACCEPTEDANDAUTHORIZED THEREQUESTEDRESOURCESAREHANDLED3EVERAL modules perform these specific tasks. Table 3-3 shows the modules that handle content directly, like static files.
109
110
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Table 3-3. Content Modules
Module Name
DLL
Description
?ceIk`qha
Cgi.dll
Executes Common Gateway Interface (CGI) processes to build response output.
@ab]qhp@k_qiajpIk`qha
$EFDOCDLL
Attempts to return a default document for requests made to the parent directory.
@ena_pknuHeopejcIk`qha
dirlist.dll
,ISTSTHECONTENTSOFADIRECTORYANDBUILDSA(4-, response of the listing.
Eo]leIk`qha
Isapi.dll
4HISMODULESHOSTSOTHER)3!0)EXTENSION$,,S
Eo]leBehpanIk`qha
Filter.dll
4HISMODULESUPPORTS)3!0)FILTER$,,S
OanranOe`aEj_hq`aIk`qha
Iis_ssi.dll
4HISMODULEISRESPONSIBLEFORSERVER SIDEINCLUDE CODE AFORMERMODULARIZATIONTECHNIQUE
Op]pe_BehaIk`qha
Static.dll
4HISMODULESERVESSTATICFILES SUCHASIMAGES
B]op?ceIk`qha
iisfcgi.dll
3UPPORTS&AST#') WHICHPROVIDESAHIGH PERFORMANCE ALTERNATIVETO#')5SEDTOSUPPORTWEBDEVELOPMENT ENVIRONMENTSLIKE0(0
CompressionISACOMMONWAYTOSAVEBANDWIDTHANDTRANSFERLARGEFILESMOREEFFICIENTLYSEE 4ABLE Table 3-4. Compression Modules
Module Name
DLL
Description
@uj]ie_?kilnaooekjIk`qha
Compdyn.dll
5SEDTOCOMPRESSRESPONSESUSINGgzip compression transfer coding on the fly
Op]pe_?kilnaooekjIk`qha
Compstat.dll
Performs the pre-compression of static content
CachingISANOTHERWAYTOIMPROVEPERFORMANCE3EVERALMODULESSTOREFILESSOTHATTHEDELIVERY process is either accelerated or eliminated altogether (see Table 3-5). Table 3-5. Caching Modules
Module Name
DLL
Description
Beha?]_daIk`qha
Cachfile.dll
0ROVIDESUSERMODECACHINGFORFILESANDFILEHANDLES
DPPL?]_daIk`qha
Cachhttp.dll
0ROVIDESKERNELMODEANDUSERMODECACHINGIN(440SYS DRIVER
Pkgaj?]_daIk`qha
Cachtokn.dll
0ROVIDESUSERMODECACHINGOFUSERNAMEANDTOKENPAIRSFOR MODULESTHATUSE7INDOWSUSERPRINCIPALS
Qne?]_daIk`qha
Cachuri.dll
0ROVIDESUSERMODECACHINGOF52,INFORMATION
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
+NOWINGWHATISHAPPENINGISESSENTIALFOR7EB3ERVERADMINISTRATORS4HEREARESEVERAL STEPSFROMTHECODEONYOURSERVERWHEREYOUPROBABLYHAVEDEBUGCAPABILITIESTOTHEBROWSER WHEREYOUMIGHTMISSSEEINGTHEDESIREDOUTPUT%VENPRODUCTIONSYSTEMSDONOTALWAYSBEHAVEAS expected. Table 3-6 shows logging and diagnostic modules, which help you understand the internal processing of requests and responses. Table 3-6. Logging and Diagnostic Modules
Module Name
DLL
Description
?qopkiHkccejcIk`qha
,OGCUSTDLL
,OADSADDITIONALCUSTOMLOGGINGMODULES
B]eha`NamqaopoPn]_ejcIk`qha
Iisfreb.dll
4HISMODULESUPPORTSTHEh&AILED2EQUEST4RACINGv feature.
DpplHkccejcIk`qha
,OGHTTPDLL
This module passes information and processing STATUSTOTHE(440SYSDRIVERFORLOGGINGPURPOSES
NamqaopIkjepknIk`qha
Iisreqs.dll
This module tracks requests currently executing in worker processes and reports information using the 2UNTIME3TATUSAND#ONTROL!PPLICATION0ROGRAMMING)NTERFACE23#!
Pn]_ejcIk`qha
Iisetw.dll
2EPORTSEVENTSTO-ICROSOFT%VENT4RACINGFOR 7INDOWS%47
Finally, you need an interface to the managed world. In Chapter 1, you saw how modules interacted. Table 3-7 shows which modules areINVOLVEDINTHISINTERACTION Table 3-7. Integration of Managed Modules
Module Name
DLL/Assembly
Description
I]j]ca`Ajceja
-ICROSOFT.%4]oe_=qpd?qopkiIk`qha6EDpplIk`qha w lq^he_rke`Ejep$Dppl=llhe_]pekj]llhe_]pekj% w ]llhe_]pekj*=qpdajpe_]paNamqaop'9± jasArajpD]j`han$pdeo*Kj=qpdajpe_]paNamqaop%7 y lq^he_rke`@eolkoa$%wy lq^he_rke`Kj=qpdajpe_]paNamqaop$k^fa_pokqn_a(Araj)p=ncoarajp=nco% w Dppl=llhe_]pekj]ll9$Dppl=llhe_]pekj%okqn_a7 Dppl?kjpatp?kjpatp9Dppl?kjpatp*?qnnajp7 ++]j`]_pekj y
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
2EMEMBERTHATYOURMODULEHASACCESSTOTHEDppl?kjpatp object and from there to all other intrinsic ASP.NET pipeline objects, such as Naolkjoa and Namqaop&ROMHERE YOUCANRETRIEVEINPUT CREATECONTENT ANDSOFORTH(OWEVER KEEPINMINDTHATCERTAINTHINGSMAYNOTBEAVAILABLEUNTIL further down the chain. 9OUCANHOOKASMANYEVENTSASYOULIKEINTHEEjep method so that your module is able to manage multiple operations with different functions. It is tidier to separate differing logic into separate modules. This ensures that the modules are, indeed, modular, as their name implies. In many CASES ANYFUNCTIONALITYTHATYOUIMPLEMENTCOULDREQUIREHOOKINGMULTIPLEEVENTS&OREXAMPLE a logging filter might log the start time of a request in >acejNamqaop and then write the request completion into the log in Aj`Namqaop.
NCaution Modules work deep inside the processing pipeline. Calling certain methods can prevent the pipeline from proceeding to the next step. In particular, Naolkjoa*Aj` and =llhe_]pekj*?kilhapaNamqaop complete the request and force the pipeline to end, thus skipping all subsequent steps. The pipeline will return control to the Web Server and no further modules will be invoked. A better practice is to leave the pipeline running, but use a context variable to inform subsequent modules not to execute. The purpose of the @eolkoa method is to clean up any resources when the module unloads ANDTORELEASEOTHERRESOURCESBEFORETHEGARBAGECOLLECTORFINALIZESTHEMODULEINSTANCE)FTHEREIS NOTHINGTODISPOSE LEAVETHEMETHODBODYBLANK The Ejep methodISTHEMAINMETHODOFINTEREST(ERE YOUCANINITIALIZEYOURMODULEANDWIRE ITUPTOONEORMOREREQUEST PROCESSINGEVENTSAVAILABLEONTHEDppl=llhe_]pekj class. Keep in mind THATEVENTSFIREWHENTHEAPPROPRIATESTEPINTHEPIPELINEISREACHED INADEFINEDORDER ANDINCONJUNCTIONWITHOTHERMODULES7ITHOUTACLEARUNDERSTANDINGOFTHEPIPELINEARCHITECTUREEXPLAINED IN#HAPTER YOULLHAVEDIFFICULTYWRITINGSOPHISTICATEDMODULESTHAT perform well.
Example—Check for a Specific Header The following example checks for a specific header, called a referrer WHICHPROVIDESINFORMATION ABOUTTHEREFERRINGPAGE0ARADOXICALLY THEREFERRERISNAMEDhREFERRERv$ONTWORRYABOUTTHEMISspelled word (the sidebar explains more).
WIKIPEDIA ON THE WORD REFERER Referer is a common misspelling of the word referrer. It is so common, in fact, that it made it into the official specification of HTTP—the communication protocol of the World Wide Web—and has therefore become a widely used industry spelling when discussing HTTP referrers. The misspelling usage is not universal; the correct spelling of “referrer” occurs in some web specifications, such as the Document Object Model. [Source: dppl6++aj*segela`e]*knc+sege+Nabanan]
)ADVISEYOUTOPAYATTENTIONTOWHICHWORDYOUREUSING
117
118
C H APT ER 3 N MODU L ES A ND HA NDL ER S
In this example, you’ll look for a specific referrerORREFERRINGPAGETHE52,OFTHEPREVIOUS page. (It’s the page containing the link to the page you’re currently processing.) The usage of the HEADERISVOLUNTARY ACCORDINGTO(440STANDARDS BUTMOSTSITESUSEITTOTRACKUSERSORMANAGE LOGGING(OWEVER SOMEPAGESARENOTINTENDEDTOBECALLEDFROMOUTSIDEOURSITE)FLINKEDFROM another page within our application, such pages will execute correctly, but if linked from anywhere else, you’ll treat that as an exception. Such external links typically occur when a user bookmarks ASPECIFICPAGEDEEPWITHINYOURAPPLICATION7HENATTEMPTINGTOOPENONEOFTHESEPAGES ITISNOT POSSIBLE BECAUSETHEREARESEVERALPREREQUISITESTEPSTOCOMPLETEBEFOREHAND5SINGAMODULELIKE THEONEIN,ISTING YOUCANCAPTURETHESEREQUESTSOUTSIDETHECOMMONPAGECODEANDREDIRECT users to a suitable page, such as one that explains appropriate usage of bookmarks. Listing 3-4. An HTTP Module That Looks for the Referer Header lq^he__h]ooNabannanIk`qha6EDpplIk`qha w nacekjEDpplIk`qhaIai^ano lq^he_rke`@eolkoa$% w ++_ha]j)ql_k`adana* y lq^he_rke`Ejep$Dppl=llhe_]pekj_kjpatp% w _kjpatp*LnaNamqaopD]j`hanAta_qpa'9± jasArajpD]j`han$_kjpatp[LnaNamqaopD]j`hanAta_qpa%7 y rke`_kjpatp[LnaNamqaopD]j`hanAta_qpa$k^fa_poaj`an(Arajp=ncoa% w Dppl=llhe_]pekj]ll9$Dppl=llhe_]pekj%oaj`an7 DpplNamqaopnamqaop9]ll*?kjpatp*Namqaop7 eb$namqaop*Qnh*Hk_]hL]pd*Aj`oSepd$@ab]qhp*]olt%% w eb$Opnejc*EoJqhhKnAilpu$namqaop*Da]`anoWNabananY%% w pdnksjasDpplAt_alpekj$0,/(>kkgi]ngejceojkp]hhksa`%7 y y y aj`nacekj y 4HISCODEASSUMESTHATYOUHAVEAPAGECALLEDORATLEASTENDINGWITHh$EFAULTASPXvLINKINGTO another page in your application. The name of the other page doesn’t matter. To test this module: 1. Configure web.configTOACTIVATETHEMODULE 2. #REATETWOPAGES $EFAULTASPXAND2EFERER4ESTASPX$EFAULTASPXHASAHYPERLINKTO 2EFERER4ESTASPX 3. #OMPILEANDSTARTTHEAPPLICATIONBYLAUNCHINGTHE$EFAULTASPXPAGE
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
4. #LICKTHEHYPERLINKON$EFAULTASPXTHE2EFERER4ESTASPXPAGEISDISPLAYED 5. "OOKMARKTHE2EFERER4ESTASPXPAGE 6. Close your browser, reopen it, and load the bookmark, then press F5 to force a refresh of the PAGEFROMTHESERVER 7. !NEXCEPTIONOCCURSANDTHEBROWSERSHOWSAERROR 9OUMIGHTINSISTTHATALLOFTHISCANBEACCOMPLISHEDONTHEPAGELEVELUSINGCONVENTIONAL CODE4HISISCORRECT BUTFUNDAMENTALTASKSAREBESTHANDLEDONAFUNDAMENTALLEVEL)NADDITION INTERCEPTINGLOW LEVELEVENTSTOHANDLELOW LEVELACTIONISFASTER MORESECURE ANDMORERELIABLE !DDINGMOREPAGESWITHTHESAMEBEHAVIORDOESNOTREQUIREANYCHANGETOTHECODE)TSIMPLY works because the module tests all pages in the application.
Interaction Between Modules 7RITINGPRIVATEMODULESISAPOWERFULTECHNIQUEFOREXTENDING!30.%4(OWEVER EXTENDINGCAN mean replacing existing functionality. Sometimes a smart solution results simply from using the internal modules and your own module together. To begin, you’ll need access to the internal modules at runtime. You should make connections in the EjepEVENTINORDERTOHAVEACCESSATANEARLYSTAGE,ISTING SHOWSYOUHOWTORETRIEVE information about internal modules, and other kinds of modules, attached to the pipeline so far. Listing 3-5. Retrieving Information About Modules lq^he__h]ooOaooekjHkcIk`qha6EDpplIk`qha w nacekjEDpplIk`qhaIai^ano lq^he_rke`@eolkoa$% w y lq^he_rke`Ejep$Dppl=llhe_]pekj]llhe_]pekj% w Dppl?kjpatp_kjpatp9Dppl?kjpatp*?qnnajp7 bkna]_d$opnejcgauej]llhe_]pekj*Ik`qhao*=hhGauo% w _kjpatp*Naolkjoa*Snepa$Opnejc*Bkni]p$w,y9w-yw.y8^n:( gau( ]llhe_]pekj*Ik`qhaoWgauY*CapPula$%*EoLq^he_;lq^he_6ejpanj]h( ]llhe_]pekj*Ik`qhaoWgauY*CapPula$%*=ooai^huMq]hebea`J]ia%%7 y y aj`nacekj y
119
120
C H APT ER 3 N MODU L ES A ND HA NDL ER S
The current context is used to output the text directly into the current page where the request is handled. You can use the Ik`qhao property to get a list of the modules and where they are defined (see Figure 3-3).
Figure 3-3. Modules already available You probably need to use this method of getting access to an embedded module, as not all MODULESOFFERDIRECTACCESSTOTHEIRSTATESANDEVENTS/NCEYOUKNOWTHENAMEANDTYPEOFASPEcific module, you can cast the type and get the object you need.
Configuration and Deployment Now that the module is implemented, you can compile it into an assembly that ASP.NET is able to load at runtime. This is straightforward as long as the module is in the web application. No SPECIALACTIONISREQUIRED9OUWILLPROBABLYWANTTOCREATESEVERALMODULESANDHAVETHEMINDIFferent assemblies for easy reuse. The assemblies will need to be referenced by your web project. To CONSTRUCTSUCHAMODULE CHOOSEh#LASS,IBRARYvASTHEPROJECTTEMPLATE2EMOVETHEDEFAULTCLASS CREATEDBYTHETEMPLATE ANDADDANOBJECTOFTYPEh!30.%4-ODULEvASSHOWNIN&IGURE
Figure 3-4. Add a module to current project.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
Configuring the Default Web Server and Development Environment To test the module, you’ll need to configure the settings in web.config. Place the appropriate lines in the 8ouopai*sa^: section: 8dpplIk`qhao: 8]``j]ia9NabannanIk`qha pula9=lnaoo*Atpajoe^ehepu*DpplIk`qhao*NabannanIk`qha+: 8+dpplIk`qhao: 4HESETTINGSFORTHEDEVELOPMENTENVIRONMENTALSOAPPLYFOR))3 ))3 AND))3INCLASSIC MODE4HEREARESEVERALADVANTAGESTORUNNINGTHE))3INTEGRATEDPIPELINEHOWEVER THATREQUIRES different settings, asSHOWNIN4ABLE Table 3-10. Options of the httpModule Settings
Attribute
Typical Values
j]ia
any string
Description The module name that appears in settings dialogs
pula
class, assembly
-ODULETYPE
Configuring IIS7 Settings In the main (web) project, add a reference to the project containing the module. Assuming the namespace of the external project is =lnaoo*Atpajoe^ehepu*DpplD]j`han*IuD]j`han, add the following to web.config: 8ouopai*sa^Oanran: 8ik`qhao: 8]``j]ia9NabannanIk`qha pula9=lnaoo*Atpajoe^ehepu*DpplD]j`han*IuD]j`hannaokqn_aPula9Beha namqena=__aoo9Na]`lna?kj`epekj9ejpacn]pa`Ik`a+: 8+ik`qhao: 8+ouopai*sa^Oanran: Compile both the project containing the module and the web project. Add the mapping in )NTERNET)NFORMATION3ERVICES-ANAGER4HEMAPPINGWILLNOWFUNCTIONPERFECTLYFORBOTHTHEDEVELOPMENTENVIRONMENTANDDIRECTUSAGE from the local IIS7 (see Table 3-11). Table 3-11. Options for the Module Settings for IIS7 Integrated Mode
Attribute
Typical Values
Description
j]ia
any string
The module name that appears in settings dialogs.
lna_kj`epekj
string
Specifies conditions under which the module will run. This is usually the name of another handler or module required to run before this one.
pula
class, assembly
Type information of the handlers definition.
121
122
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Configure Using IIS Management Console 2ATHERTHAN adding the IIS7 integrated mode settings to web.config YOUCANSIMPLYUSETHE))3-ANagement Console. The settings correspond directly. Altering web.config will result in an immediate CHANGETOTHE-ANAGEMENT#ONSOLESETTINGS ANDVICEVERSA4OCONFIGUREUSINGTHE))3-ANAGEment Console: 1. /PEN)NTERNET)NFORMATION3ERVICE-ANAGER 2. /PENTHEWEBYOUWANTTOCHANGE 3. )NTHE))3SECTION DOUBLE CLICKONTHE-ODULESICON 4. #LICKON!DD-ANAGED-ODULEINTHETASKLISTTOTHERIGHT 5. %NTERTHESEVALUESINTHEDIALOGANDCLOSETHEDIALOGBYCLICKING/+ THEN a. 'IVETHEMODULEANAPPROPRIATENAME b. /PENTHETYPEDROP DOWNANDSELECTTHEMODULESTYPE 6. #LOSETHEMAINDIALOGBYPRESSING/+ No restartISREQUIREDTOACTIVATE the new settings.
Handlers This sectionFOCUSESONDEVELOPING(440HANDLERSFOR))3 USING the .NET Framework. You’ll look ATWHENITISAPPROPRIATETODEVELOPAN))3HANDLER rather than a module.
Built-In Handlers ASP.NET offersSEVERALDEFAULT(440HANDLERS
s 0AGE(ANDLERASPX : Handles web pages
s 5SER#ONTROL(ANDLERASCX (ANDLES7EBUSERCONTROLPAGES
s 7EB3ERVICE(ANDLERASMX (ANDLES7EB3ERVICEPAGES
s 4RACE(ANDLERTRACEAXD : Handles trace functionality
s !SSEMBLY2ESOURCE,OADER7EB2ESOURCEAXD : Handles embedded resources in assemblies
s 3CRIPTRESOURCEHANDLER3CRIPT2ESOURCEAXD : Handles the scripting support for AJAXenabled projects
s &ORBIDDEN(ANDLERCONFIG $ENIESACCESSTOFILESTHATCONTAINCONFIDENTIALINFORMATION
The IIS configuration defines the assignments. You will also find other assignments there. Extensions such as .tkih, *nai, *ok]l, and *or_RELATETOTHECAPABILITIESPROVIDEDBY7INDOWS #OMMUNICATION&OUNDATION7#& AND its predecessor, .NET remoting.
Extending ASP.NET Using Http Handlers 7HILEMODULESARELOWLEVEL ANDRUNAGAINSTEVERYINBOUND request to the ASP.NET application, HTTP handlers focus more on a specific request mapping. This is usually a mapping of a file extension.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
(440HANDLERIMPLEMENTATIONSAREVERYSIMPLEINTHEIRCONCEPT BUTHAVINGACCESS to the Dppl?kjpatpOBJECTENABLESENORMOUSVERSATILITY(ANDLERSAREIMPLEMENTEDTHROUGHTHE EDpplD]j`han interface, or its asynchronous counterpart, EDppl=ouj_D]j`han. The interface consists of a single method, Lnk_aooNamqaop, and a single property, EoNaqo]^ha4HEASYNCHRONOUSVERSION has a pair of methods (>acejLnk_aooNamqaop and Aj`Lnk_aooNamqaop) and the same EoNaqo]^ha propERTY4HEVITALINGREDIENTISLnk_aooNamqaop WHICHRECEIVESANINSTANCEOFTHEDppl?kjpatp object. 4HISSINGLEMETHODISRESPONSIBLEFORHANDLINGA7EBREQUESTFROMSTARTTOFINISH (OWEVER SIMPLEDOESNOTIMPLYSIMPLISTIC!SYOUMAYKNOW THEREGULARPAGEPROCESSINGCODE ANDTHE7EB3ERVICEPROCESSINGCODEAREIMPLEMENTEDASHANDLERS"OTHAREANYTHINGBUTSIMPLE Their power originates from the Dppl?kjpatp object, which has access to both the request informaTIONANDTHERESPONSEDATA4HISMEANSTHAT LIKEA7EB3ERVER AHANDLERCANCONTROLTHEWHOLE PROCESSONITSOWN7HATEVERYOUWANTTOIMPLEMENTONTHELEVELOFSPECIFICMAPPINGISACHIEVABLE using handlers.
Scenarios to Use HTTP Handlers To betterUNDERSTANDTHEPOWEROFHANDLERS LETSTAKEALOOKATWHATOTHERSHAVEIMPLEMENTEDON top of EDpplD]j`han:
s #REATINGDYNAMICIMAGES
s 7ATERMARKINGEXISTINGIMAGES
s h0RETTYPRINTINGvOFTHEPAGESSOURCECODE
s 'ENERATINGDYNAMICCONTENTPULLEDFROMADATABASEOREXTERNALRESOURCE
s 4RANSFORMINGCONTENTFROMOTHERRESOURCES SUCHAS8-,INTO(4-,
s %XTRACTINGRESOURCESFROMASSEMBLIESONTHEFLY
s 2EDIRECTINGTOFROM33,
s )MPLEMENTING0INGBACKAND4RACKBACKCAPABILITIES EVENIFTHESITEISNOTABLOG
!DDITIONALLY YOUCANIMPLEMENTHANDLERSASYNCHRONOUSLY4HISVASTLYEXTENDSTHEPOTENTIAL usage scenarios. Since asynchronous calls are closely related to threading and performance, you looked at threading and how it could benefit from asynchronous programming in Chapter 2. In this section, I will focus more on common usages of basic HTTP handler implementations.
Getting Started For an HTTP handler, all the action occurs through a single call to Lnk_aooNamqaop. This can be as simple as: lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w _kjpatp*Naolkjoa*Snepa$DahhkSknh`%7 y 5SINGTHEDppl?kjpatpOBJECT YOUHAVEACCESSTOTHENamqaop, Naolkjoa, Oaooekj, and ?]_da OBJECTS9OUHAVEALLTHEKEYFEATURESOFAN!30.%4REQUESTATYOURDISPOSAL ANDYOUCANUSETHISTO DETERMINEWHATUSERSSUBMITTEDANDTORETURNCONTENTBACKTOTHECLIENT2EFERTO#HAPTERTOSEE why Dppl?kjpatp plays such an important role in the request-processing process.)
123
124
C H APT ER 3 N MODU L ES A ND HA NDL ER S
The key operation of the handler is to write output into the Naolkjoa object—or, more specifically, the Naolkjoa object’s KqplqpOpna]i. This output is sent back to the client. Behind the scenes—for IIS6 or classic mode—the EO=LESknganNamqaop sends the KqplqpOpna]i back to the ISAPI a_^*Snepa?heajp METHOD WHICHACTUALLYPERFORMSTHE))3OUTPUTGENERATION&OR))3)NTEGRATED-ODETHEPIPELINEIS responsible to handle the KqplqpOpna]i object. Again, refer to Chapters 1 and 2 to learn more about these steps.
Building a Handler Now, let’s build a simple handler. To do this, you define a class, which implements the Ouopai*Sa^* EDpplD]j`han interface. $ESPITETHEPROMINENCEOFTHELnk_aooNamqaop method, you’ll also need to implement a property—EoNaqo]^ha4HISPROPERTY WHICHRETURNSA"OOLEANVALUE INDICATESWHETHERTHEINSTANCE can be reused for subsequent requests. In some cases, after processing a request, your handler may NOTBEINAVALIDSTATEFORPROCESSINGFURTHERREQUESTSESPECIALLYIFDATAABOUTTHEPREVIOUSREQUEST WASSTOREDINMEMBERVARIABLES4HISISBECAUSETHE!30.%4RUNTIMECANHANDLEMANYREQUESTSAT THESAMETIME!SLONGASTHEREARETHREADSAVAILABLEINTHETHREADPOOL ANEWREQUESTWILLBEPROCESSEDEVENIFANOTHERONEISSTILLRUNNING%ACHTHREADREQUIRESANEWINSTANCEOFTHEHANDLER EVEN IFTHEHANDLERISMARKEDhISREUSABLEv7HENAREQUESTISCOMPLETE THECURRENTHANDLERINSTANCEIS RETAINEDINMEMORYANDREUSEDFORTHENEXTREQUEST4HISCANLEADTOODDBEHAVIOR DEPENDINGON the workload and on the existence of other instances of the handler. Such problems can be unpreDICTABLEANDDIFFICULTTOSIMULATEORRECOGNIZEINADEVELOPMENTENVIRONMENT &ORSTABLEANDRELIABLEBEHAVIOR YOUMIGHTASSUMETHATSETTINGTHEEoNaqo]^ha property to b]hoa is the solution. After all, this would create a new instance of the object any time a request is ABOUTTOBEPROCESSED(OWEVER DEPENDINGONHOWhINTENSIVEvYOURCODEIS THISCANLEADTOHIGHER MEMORYCONSUMPTION MORE#05WORKLOAD ANDLESSTHROUGHPUT4HEREISNOSTRICTRULEABOUTIT BUT reusing the instances is the preferred solution. Keep in mind that access to members is not exactly WHATYOUMIGHTEXPECT4HEREFORE ITISADVISABLETOAVOIDPRIVATEMEMBERSTHATHOLDDATA IFPOSSIBLE)FYOUSTILLWISHTOUSEMEMBERVARIABLES REMEMBERTHATTHEYNEEDTOBETHREADSAFE7HEN REPLACINGREGULARMEMBERSWITHSTATICMETHODS YOULLHAVETOIMPLEMENTTHREADSAFECODE)FANY of these requirements cannot be fulfilled, you should set EoNaqo]^ha to b]hoa/THERWISE THEIMPLEmentation will look like the following: lq^he_^kkhEoNaqo]^ha w cap w napqnjpnqa7 y y EoNaqo]^haSHOULDBEACONSTANT#HANGINGTHEVALUEDURINGPROCESSINGWOULDNOTHAVEANY EFFECT ASTHEVALUEISREADONCETHE handler is instantiated.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
The Entry Point The Lnk_aooNamqaop method is the main entry point for the handler. Its role is to work off the request specified by the DpplNamqaopINSTANCE FROMTHEPROVIDEDDppl?kjpatp instance, and generate an appropriate response using the DpplNaolkjoa instance. The Lnk_aooNamqaopMETHODISINVOKEDBY the .NET runtime during the Ata_qpaNamqaopD]j`han request processing stage, assuming that the mapping is able to route the request to the specific handler. This is in contrast to modules, which RECEIVEALLREQUESTSPASSINGTHROUGHTHEPIPELINE Finally, let’s implement the Lnk_aooNamqaop method, so that our handler has something to do. 4OKEEPTHINGSSIMPLE OURHANDLERWILLRETURNTHECURRENTTIMEOFTHESERVER9OUCANSPECIFYTHE TIMEZONEINTHEQUERYSTRING/URGOALISTOREQUESTA52, SUCHASdppl6++iuoanran+l]ca*peia, and OBTAINTHECURRENTTIMEOFTHESERVER)NADDITION YOUCANGETTHEUNIVERSALCOORDINATEDTIME54# by requesting dppl6++iuoanran+l]ca*peia;qp_9pnqa,ISTING SHOWSTHEIMPLEMENTATION Listing 3-6. Simple Handler Mapped to a New *.time File Extension lq^he__h]ooPeiaD]j`han6EDpplD]j`han w nacekjEDpplD]j`hanIai^ano lq^he_^kkhEoNaqo]^ha w capwnapqnjpnqa7y y lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w @]paPeia`p7 opnejcqoaQp_9_kjpatp*Namqaop*MqanuOpnejcWqp_Y7 eb$Opnejc*EoJqhhKnAilpu$qoaQp_%""qoaQp_*Amq]ho$pnqa%% w `p9@]paPeia*Qp_Jks7 y ahoa w `p9@]paPeia*Jks7 y _kjpatp*Naolkjoa*Snepa$± Opnejc*Bkni]p$8dpih:8^k`u:8d-:w,y8+d-:8+^k`u:8+dpih:(± `p*PkHkjcPeiaOpnejc$%± %%7 y aj`nacekj y !SYOUASSIGNTHISHANDLERTOASPECIFICEXTENSIONTIMEYOULLONLYRECEIVEITWHENTHECLIENT USESTHISSPECIFIC52,4HERESPONSEISSIMPLEANDCREATESASMALL(4-,PAGE9OUCOULDEVENTAILOR the response to suit clients that are not browsers.
125
126
C H APT ER 3 N MODU L ES A ND HA NDL ER S
You use the DpplNamqaop*MqanuOpnejcCOLLECTIONTORETRIEVEAQUERYSTRINGVARIABLE AND write the current time in response using the DpplNaolkjoa*Snepa method. I recommend using the KqplqpOpna]i if other handlers are processing the request, or if you want to add to the existing RESPONSE)NTHEPREVIOUSEXAMPLE YOUWRITEACOMPLETERESPONSEINTHEONEHANDLER ANDTHUS the SnepaMETHODISAPPROPRIATE&IGURE SHOWSHOWTOADDAMODULEUSINGTHE))3-ANAGER
Figure 3-5. Setting the mapping of a managed handler in IIS7 ))3DOESNOTREQUIREARESTARTORANYOTHERACTIONINORDERTOACTIVATETHEHANDLER!REQUESTTHAT uses the mapped extension should work immediately.
Example—Image Handler !VERYCOMMON scenario for handlers is the manipulation of images. As with any other resource, a browser obtains an image by sending a GET request. Handling large numbers of images at multiple resolutions can be a challenge. Imagine a web shop with thousands of product images stored at one RESOLUTION(OWEVER DIFFERENTIMAGESIZESAREREQUIREDTHROUGHOUTTHESITE FROMCATALOGTHUMBNAILS TOLARGEPREVIEWPANESANDICONSINTHESHOPPINGBASKET#ONVERTINGALLTHESEIMAGESINTOSEVERALDIFFERENTSIZESCOULDBEEXPENSIVE EVENWITHABATCHSCRIPT)MAGESCHANGEFREQUENTLY ANDMAINTAINING ALLCURRENTPICTURESINMANYDIFFERENTSOURCESIZESISANIMAGEMANAGEMENTHEADACHE!DDITIONALLY a watermark is added to the image to protect the intellectual properties of the image’s owner. 7RITINGCODEANDCREATINGIMAGESDYNAMICALLYISATYPICALTASKFORAHANDLER!TTACHINGREQUESTS TOANIMAGECOULDBEACHIEVEDBYUSINGAPATHFILTERLIKE&*ljc,ISTING SHOWSHOWEASYITISTO manipulate content and send it to a client.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
Listing 3-7. Adding a Watermark to an Image and Resizing It Using a Handler j]iaol]_a=lnaoo*Atpajoe^ehepu*DpplD]j`han w lq^he__h]ooEi]caD]j`han6EDpplD]j`han w nacekjEDpplD]j`hanIai^ano lner]pa_kjopbhk]pBKJPOEVA93.B7 lner]pa_kjopopnejcBKJP9Ran`]j]7 lner]pa_kjopopnejcPATP9S]pani]ng7 lq^he_^kkhEoNaqo]^ha w capwnapqnjpnqa7y y lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w ++`apanieja]jei]canamqaop eb$$L]pd*Cap@ena_pknuJ]ia$± _kjpatp*Namqaop*Qnh*=^okhqpaL]pd%%*Aj`oSepd$Ei]cao%% w ++hk]`ei]ca]j`]``s]pani]ng >epi]leic9$>epi]l%>epi]l*BnkiBeha$± _kjpatp*Oanran*I]lL]pd$_kjpatp*Namqaop*Qnh*=^okhqpaL]pd%%7 Cn]lde_oc9Cn]lde_o*BnkiEi]ca$eic%7 >nqod^9jasOkhe`>nqod$?khkn*Oehran%7 Bkjpb9jasBkjp$BKJP(BKJPOEVA%7 OevaBopnejcIa]oqna9c*Ia]oqnaOpnejc$PATP(b%7 ++_]h_qh]papdaopnejclkoepekjpk_ajpankqplqp bhk]pt(u7 t9eic*Se`pd+.)opnejcIa]oqna*Se`pd+.7 u9eic*Daecdp+.)opnejcIa]oqna*Daecdp+.7 c*@n]sOpnejc$PATP(jasBkjp$BKJP(BKJPOEVA%(^(t(u%7 ++naoevapklnkre`a``]p]$bnkiMqanuOpnejc% eb$_kjpatp*Namqaop*MqanuOpnejcWsY9jqhh"" _kjpatp*Namqaop*MqanuOpnejcWdY9jqhh% w ejps(d7 eb$Ejp/.*PnuL]noa$_kjpatp*Namqaop*MqanuOpnejcWsY(kqps%"" Ejp/.*PnuL]noa$_kjpatp*Namqaop*MqanuOpnejcWdY(kqpd%% w eic9$>epi]l%eic*CapPdqi^j]ehEi]ca$s(d(jqhh(EjpLpn*Vank%7 y y ++kqplqppkpdanaolkjoaopna]i eic*O]ra$_kjpatp*Naolkjoa*KqplqpOpna]i(Ei]caBkni]p*Flac%7 eic*@eolkoa$%7 y y y aj`nacekj y y
127
128
C H APT ER 3 N MODU L ES A ND HA NDL ER S
4HEHANDLERFIRSTCHECKSTHATITISDEALINGWITHAFILEFROMAPARTICULARDIRECTORYSEE,ISTING 7HILETHEFILEMAPPINGFORCESTHEHANDLERTORUNFOREVERYREQUESTFORANIMAGEWITHTHESPECIFIED extension, this test restricts the special processing to images in the “Images” folder only. The handler LOADSTHEIMAGEFROMDISK RESOLVINGTHELOCALPATHVIAOanran*I]lL]pd. Then it applies the watermark to the image. The Ia]oqnaOpnejc methodMEASURESTHESTRINGSIZEINORDERTOALIGNITWITHTHEIMAGE SOTHATTHETEXTAPPEARSCENTEREDHORIZONTALLYANDVERTICALLY3TREAMSSIMPLIFYTHEOUTPUTOFTHEIMAGE The O]raMETHODSENDSTHEOUTPUTDIRECTLYINTOANOUTPUTSTREAMIN*0%'FORMAT$ISPOSINGOFTHE IMAGEISREQUIRED ASFREQUENTUSEOFAHANDLERONASYSTEMWITHHIGHWORKLOADCOULDPREVENTTHEGARbage collector from freeing the memory often enough. Listing 3-8. The Handler Is As Easy to Use As Any Other Image Resource 8]ol6Ei]canqj]p9oanranE@9eic-Ei]caQnh9z+Ei]cao+Eic-*ljc+:8^n+: 8]ol6Ei]canqj]p9oanranE@9Ei]ca-Ei]caQnh9z+Ei]cao+Eic-*ljc;s9-,,"d9-,,+: Figure 3-6 shows the output of the watermarked and thumbnail images.
Figure 3-6. Add a watermark to an image on the fly or create a thumbnail. $YNAMICIMAGEMANIPULATIONISPOWERFULANDFLEXIBLE ANDTHEREARENUMEROUSPOSSIBILITIESFOR using an image handler in your applications. The final step in creating such a handler is to configure it in web.config0LEASEREFERTOTHESECTIONh#ONFIGURATIONAND$EPLOYMENTvTOREADMOREABOUT the VARIOUSSETTINGS
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
Example—Read Dynamic CSS from Resource )N,ISTING )DESCRIBEAHANDLERTHATLOADSSTYLESHEETSSTOREDASEMBEDDEDRESOURCESINAN ASSEMBLY)TREPLACESANYCALLTOASTYLESHEETBYDELIVERINGASPECIFICCSSFILE4HEDEFINITIONINTHE web.config looks like this: 8]``ran^9CAPl]pd9&*_oopula9=lnaoo*Atpajoe^ehepu*DpplD]j`han*?ooD]j`han+: 3EETHESECTIONh#ONFIGURATIONAND$EPLOYMENTvFORMOREINFORMATIONABOUTCONFIGURING a handler for the IIS7 integrated mode. 4OOBTAINTHESTYLEFILES THEYMUSTBEMARKEDASANEMBEDDEDRESOURCE4HEREARESEVERALWAYS to handle data from sources other than the file system (see Figure 3-7).
. Figure 3-7. To handle files as embedded resources, use the file’s Property box. 4HECODEITSELFDOESNOTHAVEANYQUIRKS)TPROCESSESWHATEVERITENCOUNTERS ANDDECIDESHOW to proceed from the information in Dppl?kjpatpSEE,ISTING
129
130
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Listing 3-9. Dynamic Handling of CSS from Embedded Resources lq^he__h]oo?ooD]j`han6EDpplD]j`han w nacekjEDpplD]j`hanIai^ano lq^he_^kkhEoNaqo]^ha w capwnapqnjb]hoa7y y lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w Qji]j]ca`IaiknuOpna]io7 eb$_kjpatp*Namqaop*Qoan=cajp9jqhh"" _kjpatp*Namqaop*Qoan=cajp*?kjp]ejo$IOEA%% w o9$Qji]j]ca`IaiknuOpna]i%± pdeo*CapPula$%*=ooai^hu*CapI]jebaopNaokqn_aOpna]i$± =lnaoo*DppD]j`han*D]j`han=ooai^hu*?oo*ea*_oo%7 y ahoa w o9$Qji]j]ca`IaiknuOpna]i%± pdeo*CapPula$%*=ooai^hu*CapI]jebaopNaokqn_aOpna]i$± =lnaoo*DppD]j`han*D]j`han=ooai^hu*?oo*bb*_oo%7 y o*Oaag$,(OaagKnecej*>acej%7 qoejc$IaiknuOpna]iio9jasIaiknuOpna]i$$ejp%o*Hajcpd%% w ^upaWY^qbban9jas^upaWo*HajcpdY7 o*Na]`$^qbban(,(^qbban*Hajcpd%7 io*Snepa$^qbban(,(^qbban*Hajcpd%7 io*SnepaPk$_kjpatp*Naolkjoa*KqplqpOpna]i%7 y y aj`nacekj y First, let’s locate the current user agent in this code by using the property Qoan=cajp of the DpplNamqaop object. If the Qoan=cajpISh-3)% vINDICATINGAN)NTERNET%XPLORERBROWSER THENTHE RESOURCENAMEDhIECSSvISRETRIEVEDFROMTHEASSEMBLY&ORANYOTHERQoan=cajpo, “ff.css” is loaded instead. The stream is copied to a IaiknuOpna]i object. The IaiknuOpna]i is helpful, as it is able to copy its own content to another stream by using the SnepaPk method. In the case of a handler, this is THEOUTPUTSTREAMPROVIDEDBYTHEDpplNaolkjoa object through the Naolkjoa property. This technique can be reused. By looking for specific file extensions and for the “user-agent” header, you can block certain clients from reading the content of these resources or supply them WITHAREPLACEMENTRESOURCE!LTERNATIVELY READINGRESOURCESFROMADATABASEINSTEADOFANASSEMBLYGIVESMOREFLEXIBILITYWITHOUTADDINGCODETOTHEPAGESTHEMSELVES
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
USING WEB RESOURCES The example shown in this section is just to explain typical scenarios for HTTP handlers. Especially regarding web resource management, the preferred method for storing and retrieving resources is the embedding of web resources. Using an assembly bound attribute, you can add any kind of data to the assembly: W]ooai^hu6Sa^Naokqn_a$?oo*ea*_oo(patp+_oo%Y W]ooai^hu6Sa^Naokqn_a$?oo*bb*_oo(patp+_oo%Y An embedded handler for the designated *]t` extension retrieves the resources from server. The URL uses two query string values, “d” and “t”. “d” takes an encrypted ID of that value and “t” a time stamp. To retrieve such a resource, you can create the URL, using the following code snippet from a page: ?heajpO_nelpI]j]can_o9L]ca*?heajpO_nelp7 PulanoPula9pdeo*CapPula$%7 DpihHejgdh?oo9jasDpihHejg$%7 dh?oo*Dnab9_o*CapSa^Naokqn_aQnh$noPula(?oo*ea*_oo%7 pdeo*?kjpnkho*=``$dh?oo%7 The call of the CapSa^Naokqn_aQnh method assures that the parameters for query string are set properly.
Example—Handler That Does Not Create Content Handlers usuallyCREATECONTENTTYPICALLY(4-,ORDATATHATBUILDSANIMAGE(OWEVER ITSNOT IMPERATIVE(ANDLERSAREPRIMARILYINVOKEDBYTHEASSOCIATEDFILEEXTENSION)MAGINEYOUDEFINE a custom extension named “.counter” and associate it with a handler. Your handler might execute SOMECODE BUTIGNORETHE7RITERANDNOTSENDANYTHINGBACKTOTHECLIENT!NOTHERIDEAISTOONLY WRITEDATATOTHECLIENTWHENYOURUNTHEHANDLERINDEBUGMODEANDSAVEBANDWIDTHWHENRUNNING ONAPRODUCTIONSERVER7HETHERORNOTTHEOUTPUTISREQUIRED YOULLSTILLHAVETOINVOKETHEHANDLER BYISSUINGA'%4OR0/34REQUESTTOTHESERVER4HEREFORE YOULLNEEDANELEMENTTHATCANFORCETHE browser to create such a request. There are only a few ways to do this:
s )MAGEELEMENT
s &ORMELEMENT
s )&RAMEELEMENT
s *AVA3CRIPTCODE
)MUSINGAN)&2!-%ELEMENT in the following example, because I want to show the content WHENINDEBUGMODE ANDLEAVETHECONTENTEMPTY OTHERWISE4HEDEFINITIONOFTHE)&2!-%DEMONSTRATESHOWYOUINVOKETHEHANDLER ASSHOWNIN,ISTING Listing 3-10. Invoke a Handler Using a Registered Extension 8ebn]iaon_9iu*_kqjpanse`pd9-,,daecdp91,o_nkhhejc9jk:8+ebn]ia: Furthermore, the handler itself shows the processing within the Lnk_aooNamqaop method (see ,ISTING
131
132
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Listing 3-11. Code of the Handler lq^he__h]oo?kqjpanD]j`han6EDpplD]j`han w nacekjEDpplD]j`hanIai^ano lq^he_^kkhEoNaqo]^ha w capwnapqnjpnqa7y y lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w opnejcl]pd9_kjpatp*Namqaop*QnhNabannan*Hk_]hL]pd7 opnejcbeha9_kjpatp*Oanran*I]lL]pd$?kqjpan*tih%7 T@k_qiajp_jp@k_9T@k_qiajp*Hk]`$beha%7 r]n_jp9$bnkiaej_jp@k_*Nkkp*Ahaiajpo$l]ca%sdana± a*=ppne^qpa$l]pd%*R]hqa*Amq]ho$l]pd%± oaha_pa%*BenopKn@ab]qhp8TAhaiajp:$%7 eb$@a^qccan*Eo=pp]_da`% w ++Ej`a^qcik`ana]`kjhu]j`_na]pakqplqp ++na]`ejcTIHeopdna]`o]ba _kjpatp*Naolkjoa*Snepa$Opnejc*Bkni]p$?kqjpan9w,y(± _jp*=ppne^qpa$_kqjp%*R]hqa%%7 y ahoa w ++ejlnk`q_pekjfqopopknar]hqao Na]`anSnepanHk_gnsh9jasNa]`anSnepanHk_g$%7 nsh*=_mqenaSnepanHk_g$PeiaOl]j*BnkiOa_kj`o$.%%7 eb$_jp99jqhh% w ++l]ca`kaojkpateopuap TAhaiajpjasL]ca9jasTAhaiajp$l]ca( jasT=ppne^qpa$_kqjp(-%( jasT=ppne^qpa$l]pd(l]pd%%7 _jp@k_*Ahaiajp$L]cao%*=``$jasL]ca%7 y ahoa w ++ej_na]oa_kqjpan(oappeiaop]il ejpe9Ejp/.*L]noa$_jp*=ppne^qpa$_kqjp%*R]hqa%7 _jp*=ppne^qpa$_kqjp%*R]hqa9$''e%*PkOpnejc$%7 y _jp@k_*O]ra$beha%7 nsh*Naha]oaSnepanHk_g$%7 y y aj`nacekj y 4HEHANDLERSTORESTHENUMBEROFPAGEREQUESTSINASINGLE8-,DOCUMENT/THERDEVELOPERSCANADDTHE)&2!-% code to the pages they want included in the counter total. Internally, the file access is shared read mode. The BehaOpna]i class used behind the scenes in T@k_qiajp*Hk]`
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
SUPPORTSREADINGFROMDIFFERENTTHREADS(OWEVER WRITEACCESSREQUIRESANEXCLUSIVELOCK BLOCKINGOTHERTHREADS4OACHIEVETHIS ANa]`anSnepanHk_g class monitors the threads and blocks other THREADSFROMACCESSINGTHEFILEDURINGWRITEMODE4HISISNTAVERYEFFICIENTMETHOD)NAREAL LIFE SCENARIO CONSIDERREPLACINGTHE8-,ACCESSWITHADATABASEOPERATION!FTEROBTAININGACCESSTOTHE 8-, ,).1TO8-,ISUSEDTOEITHERCREATEELEMENTSFORTHEFIRSTTIMEORADDTOTHEEXISTINGCOUNTER when the page is requested again. In the example, you want to distinguish between debug mode and production mode. You can ascertain debug mode by using the @a^qccan*Eo=pp]_da` property. In debug mode, the content OFTHE)&2!-%ISFILLEDWITHTHECOUNTERFORTHECURRENTPAGE4HE,).1STATEMENTPREPAREDATTHE beginning of the Lnk_aooNamqaop method returns either jqhh or the element containing the counter information. Figure 3-8 shows the output in debug mode. The counter does not increase for requests in debug mode. In production mode, the counter increases but no output displays.
Figure 3-8. Output of the handler in debug mode )NTHISEXAMPLE )SHOWEDAHANDLERTHATDOESNTCREATECONTENTWITHEVERYUSE4HECONCEPTS behind this are:
s $EFININGHOWANDWHERETOINVOKETHEHANDLER
s 2EMEMBERINGTHATCONCURRENTTHREADSACCESSTHECODE
s 2ECOGNIZINGTHATCREATING output is not mandatory
Example—Using IHttpHandlerFactory to Perform URL Rewriting In this example, I’ll introduce another interface. For more flexibility with creating handlers on the fly, the EDpplD]j`hanB]_pknuINTERFACEISAVAILABLE!CLOSERLOOKATTHEDEFAULTL]caD]j`hanB]_pknu SHOWSTHATITISNOTDERIVEDFROMEdpplD]j`han, but from EDpplD]j`hanB]_pknu. This class is used to process the regular *]olt pages. 9OUDONTHAVETOCREATEAFACTORY BUTITGIVESMORECONTROLOVERTHECREATIONPROCESS)TCANBE useful in more complex scenarios. First, let’s look into the interface definition: lq^he_ejpanb]_aEDpplD]j`hanB]_pknu w EDpplD]j`hanCapD]j`han$Dppl?kjpatp_kjpatp(± opnejcnamqaopPula(± opnejcqnh(± opnejcl]pdPn]joh]pa`%7 rke`Naha]oaD]j`han$EDpplD]j`hand]j`han%7 y
133
134
C H APT ER 3 N MODU L ES A ND HA NDL ER S
NNote If you decode this using Reflector, you may see another interface named EDpplD]j`hanB]_pknu.. This provides another overload for the CapD]j`han method. You don’t need this at this stage, as you’ll only be using the EDpplD]j`hanB]_pknu. )MAGINEYOUHAVEAPAGEWITHCONTENTSUCHAS 8!9@]paPeia*Jks*PkOpnejc$I%!: 3ETTHECURRENTCULTUREBYADDINGTHECULTURE)$TOTHE52,4YPICAL52,SLOOKLIKETHIS
s dppl6++hk_]hdkop+?d]lpan,/+QnhNasnepaB]_pknuD]j`han+@ab]qhp*]olt
s dppl6++hk_]hdkop+?d]lpan,/+QnhNasnepaB]_pknuD]j`han+aj)qo+@ab]qhp*]olt
s dppl6++hk_]hdkop+?d]lpan,/+QnhNasnepaB]_pknuD]j`han+`a)`a+@ab]qhp*]olt
s dppl6++hk_]hdkop+?d]lpan,/+QnhNasnepaB]_pknuD]j`han+bn)bn+@ab]qhp*]olt
/URGOALISTOFINDAWAYTOEXTRACTTHECULTURECODESUCHAShDE DEvORhEN USv FROMTHE52, set it as the current culture of the thread, and process the page without the culture code, as shown INTHEFIRST52,,ISTING SHOWSAFACTORYTHATCREATESTHEREQUIREDINSTANCES Listing 3-12. Using a Factory to Rewrite a URL lq^he_]^opn]_p_h]ooNasnepaB]_pknuD]j`han6EDpplD]j`hanB]_pknu w lnkpa_pa`NasnepaB]_pknuD]j`han$% 6^]oa$% w y EDpplD]j`hanEDpplD]j`hanB]_pknu*CapD]j`han$Dppl?kjpatp_kjpatp(± opnejcnamqaopPula(± opnejcqnh(± opnejcl]pdPn]joh]pa`% w L]enp]ncap9CapNai]lEjbk$_kjpatp(namqaopPula(qnh(l]pdPn]joh]pa`%7 opnejcbehaj]ia9_kjpatp*Oanran*I]lL]pd$p]ncap*Benop*PkOpnejc$%%7 _kjpatp*NasnepaL]pd$qnh(qnh(p]ncap*Oa_kj`*PkOpnejc$%%7 EDpplD]j`han]llD]j`han9± L]caL]noan*Cap?kileha`L]caEjop]j_a$p]ncap*Benop*PkOpnejc$%(± behaj]ia(_kjpatp%7 napqnj]llD]j`han7 y rke`EDpplD]j`hanB]_pknu*Naha]oaD]j`han$EDpplD]j`hand]j`han% w y lnkpa_pa`]^opn]_pL]enCapNai]lEjbk$Dppl?kjpatp_kjpatp(opnejcnamqaopPula(± opnejcqnh(opnejcl]pdPn]joh]pa`%7 y lq^he__h]oo?qhpqnaNasnepaD]j`han6NasnepaB]_pknuD]j`han w
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
lnkpa_pa`kranne`aL]enCapNai]lEjbk$Dppl?kjpatp_kjpatp(± opnejcnamqaopPula(± opnejcqnh(± opnejcl]pdPn]joh]pa`% w opnejcknecej]hL]pd9Dppl?kjpatp*?qnnajp*Namqaop*L]pd7 opnejcopaiL]pd9+7++At]ilha(nalh]_asepdrenpq]h`ena_pknueb]ju opnejcjasL]pd9knecej]hL]pd*Oq^opnejc$knecej]hL]pd*Ej`atKb$opaiL]pd%'± opaiL]pd*Hajcpd%7 opnejcWYoaciajpo9jasL]pd*Olhep$#+#%7 opnejcmqanuOpnejc9Dppl?kjpatp*?qnnajp*Namqaop*Qnh*Mqanu7 pnu w opnejch]jcq]caL]np9oaciajpoW,Y7 ?qhpqnaEjbk_e9jas?qhpqnaEjbk$h]jcq]caL]np%7 Ouopai*Pdna]`ejc*Pdna]`*?qnnajpPdna]`*?qnnajp?qhpqna9_e7 napqnjjasL]en$+'opaiL]pd'opnejc*Fkej$+(oaciajpo(-(± oaciajpo*Hajcpd)-%(mqanuOpnejc%7 y _]p_d$JqhhNabanaj_aAt_alpekj% w y napqnjjasL]en$knecej]hL]pd(mqanuOpnejc%7 y y The definition in web.config or IIS settings is the same as for any other handler. This means that the ASP.NET engine will accept both EDpplD]j`han and EDpplD]j`hanB]_pknu in order to obtain access to the handler object. The code is simplified for clarity. You can extend the error handling by adding code to handle THESTEMPATH EVENIFITSSETTOAVALUEOTHERTHANTHEROOTPATH4HECOREIMPLEMENTATIONISAROUND the CapD]j`han method, which returns the used handler. If resources are blocked and need to be freed or disposed of, the required code will appear in the Naha]oaD]j`han method. As shown in the example, this is not always necessary. Keep in mind that the memory consumption of the handler MIGHTBEANISSUEIFTHESERVERHASAHIGHWORKLOAD INWHICHCASERELEASINGRESOURCESCOULDHELP &IGURE SHOWSTHEBEHAVIORFORSEVERALLANGUAGES52,REWRITINGISAFLEXIBLE SEARCH ENGINE FRIENDLYMETHODOFMODIFYINGBEHAVIOR
Figure 3-9. Use URL rewriting to set the current culture of a Web application.
135
136
C H APT ER 3 N MODU L ES A ND HA NDL ER S
4HISEXAMPLEDEMONSTRATEDAVERYBASICHANDLERFACTORY)NTHEABSTRACTBASECLASS THECapD]j`han method rewrites using Dppl?kjpatp*NasnepaL]pd4HEDEFAULTPAGEHANDLERISSUBSEQUENTLYRETRIEVEDAND returned. This is required because otherwise, the processing of all *]olt pages is remapped to the new FACTORY2EDEFININGTHEINTERNALHANDLERWITHYOUROWNONEREPLACESTHEMAPPING ASONLYONEHANDLER CANPROCESSASPECIFICREQUEST2EMAPPINGTHEDEFAULTHANDLERREQUIRESEITHERACOMPLETEIMPLEMENTATIONOFAHANDLERWITHSIMILARBEHAVIOR ORCREATINGTHEORIGINALHANDLERANDRETURNINGITUSINGAFACTORY FromTHEPERSPECTIVEOFEXTENSIBILITY THE latter is the better option.
Advanced Usage of Handlers The standardHANDLERSCOVERMOST BUTNOTALL TASKS(TTPHANDLERS can go further. In this section, I’ll DISCUSSADVANCED extensibility topics:
s !CCESSINGTHESESSIONSTATE
s $YNAMICALLYDEALINGWITHHANDLERSINTHEPIPELINE
Handlers and Session State Handlers areLOW LEVELPROGRAMMINGCONSTRUCTS4HEYARECRITICALFOROVERALLPERFORMANCEANDIF BADLYWRITTENORCONFIGURED COULDDEGRADETHESERVERSTHROUGHPUT7HILETHEREAREWAYSTODEAL with long-running threads in the handler code (as you saw in Chapter 2), it’s preferable to write HANDLERSTHATRUNASFASTASPOSSIBLE4OMAXIMIZEHANDLERSPEED -ICROSOFTREMOVEDSESSIONINFORMATIONFROMTHEDEFAULTHANDLERS4HEPREVIOUSEXAMPLESSHOWUSEFULTASKSACCOMPLISHEDWITHOUT needing session information. )FYOUDONEEDTOACCESSSESSIONSTATEINFORMATION ITISAVAILABLEBYIMPLEMENTINGONEOFTHE following two interfaces:
s ENamqena`OaooekjOp]pa
s ENa]`KjhuOaooekjOp]pa
It’s possible to obtain session information with minimal performance loss. If you only require read access to the session data, the ENa]`KjhuOaooekjOp]pa is ideal. ENamqena`OaooekjOp]paGIVESFULL ACCESSTOALLSESSIONDATA7HENADDINGEITHERINTERFACETOYOURCLASS YOULLNOTICETHAT6ISUAL3TUDIO does not attempt to implement any method bodies. Both interfaces are simply marker interfaces that modify the internal processing within the base class. Your class declaration should look like this: lq^he__h]ooPeiaD]j`hanSepdOaooekj6EDpplD]j`han(ENamqena`OaooekjOp]pa 4HESESSIONINFORMATIONISNOWPROVIDEDTOTHEDppl?kjpatpOBJECT ANDAVAILABLETHROUGHTHE _kjpatpPARAMETEROFTHEENTRYMETHOD,ISTING PASSESAVALUETHROUGHASESSIONVARIABLETOTHE handler. Listing 3-13. The Handler Implementation with Session Support lq^he__h]ooPeiaD]j`hanSepdOaooekj6EDpplD]j`han(ENa]`KjhuOaooekjOp]pa w nacekjEDpplD]j`hanIai^ano lq^he_^kkhEoNaqo]^ha w capwnapqnjpnqa7y y
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w @]paPeia`p7 opnejcqoaQp_9_kjpatp*Namqaop*MqanuOpnejcWqp_Y7 eb$Opnejc*EoJqhhKnAilpu$qoaQp_%""qoaQp_*Amq]ho$pnqa%% w `p9@]paPeia*Qp_Jks7 y ahoa w `p9@]paPeia*Jks7 y _kjpatp*Naolkjoa*Snepa$± Opnejc*Bkni]p$_kjpatp*OaooekjWBkni]pOpnejcY*PkOpnejc$%(± `p*PkHkjcPeiaOpnejc$%± %%7 y aj`nacekj y 4HEONLYDIFFERENCEFROMTHEPREVIOUSEXAMPLEISTHESESSIONVALUENAMEDh&ORMAT3TRINGv RETRIEVEDFROM_kjpatp*OaooekjOBJECT)NTHEAPPLICATION YOUCANSETTHATVERYVALUETOCONTROLTHE BEHAVIOROFTHEHANDLER&ORPERFORMANCEPURPOSES THEENa]`KjhuOaooekjOp]pa interface is used because only read access is required.
Accessing the Pipeline Using the Context You can use these three properties of the Dppl?kjpatpOBJECTTOFURTHERMODIFYTHEBEHAVIOROFTHE HANDLERORTORETRIEVEMOREINFORMATIONABOUTWHATISTAKINGPLACE
s _kjpatp*D]j`han
s _kjpatp*LnarekqoD]j`han
s _kjpatp*Nai]lD]j`han
5SINGTHED]j`han property YOUHAVEACCESSTOTHECURRENTHANDLEREMPLOYEDINTHECURRENT CONTEXT3INCETHECONTEXTISAVAILABLEASASTATICPROPERTY ITSEASYTOACCESSTHEHANDLERINCLASSES defined elsewhere. This also applies to the LnarekqoD]j`han property, a property that is set when the HANDLERISREMAPPED2EMAPPING a handler might occur in complex scenarios where a default hanDLERPROCESSESALLREQUESTS BUTREMAPSTOANOTHERHANDLERUNDERCERTAINCIRCUMSTANCES,ISTING demonstrates this technique. Listing 3-14. Remapping to Another Handler lq^he__h]ooNai]lD]j`han6EDpplD]j`han w nacekjEDpplD]j`hanIai^ano lq^he_^kkhEoNaqo]^ha w capwnapqnjpnqa7y y
137
138
C H APT ER 3 N MODU L ES A ND HA NDL ER S
lq^he_rke`Lnk_aooNamqaop$Dppl?kjpatp_kjpatp% w EDpplD]j`hannai]lD]j`han9jqhh7 ++`apanieja]jei]canamqaop]j`d]j`hasepdlner]pad]j`han eb$$L]pd*CapAtpajoekj$_kjpatp*Namqaop*Qnh*=^okhqpaL]pd%%*Amq]ho$*ljc%% w nai]lD]j`han9jasEi]caD]j`han$%7 y ahoa w ++lnk_aoo]jukpdannamqaopsepd`ab]qhpd]j`han opnejcrenpq]hL]pd9_kjpatp*Namqaop*Qnh*=^okhqpaL]pd7 opnejcbehaj]ia9Dppl?kjpatp*?qnnajp*Namqaop*L]pd7 nai]lD]j`han9L]caL]noan*Cap?kileha`L]caEjop]j_a$renpq]hL]pd(± behaj]ia(_kjpatp%7 y _kjpatp*Nai]lD]j`han$nai]lD]j`han%7 y aj`nacekj y In this code, the handler searches for *ljcEXTENSIONSANDASSIGNSPRIVATEHANDLERSTOPROCESS THEM/THERWISE YOUUSETHESTANDARDPAGEPROCESSINGNOOTHERCUSTOMCODEISINVOLVED3IMPLY call _kjpatp*Nai]lD]j`han$nai]lD]j`han% to assign to a different handler. All internal processing is redirected to the new handler from the beginning of the pipeline. As there’s noADDITIONALOVERHEAD there are no performance issues with the handler remapping.
Asynchronous Pages The processing of pages relies on just another kind of built-inHANDLER7HENPROCESSINGPAGES YOU HAVEOFTENLENGTHYOPERATIONSAGAINSTDATABASES 7EB3ERVICES OREXTERNALDATASOURCESTHATCOULD BLOCKYOURWORKERTHREADS(EREYOUMIGHTWONDERWHETHERITSPOSSIBLETOPROCESSEVENREGULAR pages asynchronously. Fortunately, the Framework already implements this for us, and there is nothing to do but set a property.
Prepare Pages for Asynchronous Operation Building asynchronous pages is simple. Begin by including an attribute in the page’s acej method and an Aj` method, as shown in the following code: =``KjLnaNaj`an?kilhapa=ouj_$ jas>acejArajpD]j`han$>acejIapdk`%( jasAj`ArajpD]j`han$Aj`Iapdk`% %7 The page runs through its normal processing life cycle until shortly after the LnaNaj`an event fires. Then ASP.NET calls the >acejIapdk` just registered. The method launches an asynchronous operation and returns immediately. This is the lengthy operation previously mentioned that might require more time. At this point, the thread assigned to the request returns to the thread pool. Furthermore, the method returns an E=ouj_Naoqhp that allows ASP.NET to determine when the asynchronous operation has been completed. ASP.NET then extracts a thread from the thread pool and calls your End method (Aj`Iapdk`). After this returns, it executes the remaining portion of the page’s life cycle. This might sound confusing—getting the thread back from thread pool and blocking the thread again. However, between the time >acej returns and Aj` is called, the request-processing thread is free to serve other requests. Keep in mind that the page processing usually takes only a few milliseconds. The time-consuming operation in the asynchronous handler might run for seconds. The process of freeing the thread pool thread for this time allows ASP.NET to process hundreds, if not thousands, of regular pages on this very same thread. However, until Aj` is called, the rendering of the current asynchronous page is delayed. This is the same as in the previous example. It improves the situation not only for one user, but also for all users. In the sample code, you might look for the E=ouj_Naoqhp implementation. Instead of implementing our own version, you take one that the Framework implements for us. The complete code of such an example would look like the following: lq^he_l]npe]h_h]oo[@ab]qhp6Ouopai*Sa^*QE*L]ca w ?qnnaj_u?kjranpkn__7 lnkpa_pa`rke`L]ca[Hk]`$k^fa_poaj`an(Arajp=ncoa% w y E=ouj_Naoqhp>acej?kjranoekj$k^fa_poaj`an(Arajp=ncoa( =ouj_?]hh^]_g_^(k^fa_pop]pa% w __9jas?qnnaj_u?kjranpkn$%7 napqnj__*>acej?kjranoekjN]pa$?qnnaj_u*QO@(?qnnaj_u*AQN(_^(op]pa%7 y
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
rke`Aj`?kjranoekj$E=ouj_Naoqhp]n% w `kq^hanaoqhp9__*Aj`?kjranoekjN]pa$]n%7 `kq^ha]ikqjp9@kq^ha*L]noa$ptp=ikqjp*Patp%7 h^hNaoqhp*Patp9$naoqhp&]ikqjp%*PkOpnejc$%7 y
lnkpa_pa`rke`^pjOaj`[?he_g$k^fa_poaj`an(Arajp=ncoa% w =``KjLnaNaj`an?kilhapa=ouj_$ jas>acejArajpD]j`han$>acej?kjranoekj%( jasAj`ArajpD]j`han$Aj`?kjranoekj%%7 y y Here the asynchronous methods are registered with the Button’s click event. >acej?kjranoekjN]pa and Aj`?kjranoekjN]pa are the methods used to invoke the service and get the result back.
Configuration and Deployment 4OCREATEAHANDLER YOUCHOSEh#LASS,IBRARYvASTHEPROJECTTEMPLATE REMOVED the default class CREATEDBYTHETEMPLATE ANDADDEDANOBJECTOFTYPEh!30.%4(ANDLERv.OWTHATYOUHAVE implemented the handler, you can compile it into an assembly loaded by ASP.NET at runtime (see Figure 3-12). As long as the handler remains in the web application, this is simple. No special action ISREQUIRED9OULLPROBABLYWANTTOIMPLEMENTSEVERALHANDLERSANDKEEPTHEMINDIFFERENTASSEMblies for easy reuse. Simply reference the assemblies in your web project.
Figure 3-12. Add a handler to the current project
143
144
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Configuring Default Web Server and Development Environment To test the handler, you’ll need to configure the settings in web.config. Place the appropriate settings in the 8ouopai*sa^: section: 8dpplD]j`hano: 8]``ran^9CAPl]pd9&*ljcpula9=lnaoo*DpplD]j`han*Ei]caD]j`han+: 8+dpplD]j`hano: )NCASEYOUHAVEASIMILARHANDLERDEFINITIONINTHEUPSIDECONFIGURATIONPATHYOUMIGHTCONsider using either 8naikra+: or 8_ha]n+:TAGSTOOVERWRITETHEVALUESPROPERLY 4HESETTINGSFORTHEDEVELOPMENTENVIRONMENTALSOAPPLYTO))3 ))3 AND))3INCLASSICMODE 4HEREARESEVERALADVANTAGESASEXPLAINEDIN#HAPTERTORUNNINGTHE))3INTEGRATEDPIPELINE HOWEVER WHICHINVOLVESTHEDIFFERENTSETTINGS shown in Table 3-12. Table 3-12. Options of the httpHandler Settings
Attribute
Typical Values
Description
ran^
'%4 0/34
4HEHANDLERRESPONDSTOTHE(440VERBSONLY
l]pd
full path or wildcards
The path that defines the requests the handlers responds to.
pula
class, assembly
Type information of the handler’s definition.
Configuring IIS7 Settings In the main (web) project, add a reference to this project. If the namespace of the external project is =lnaoo*DppD]j`han*Ei]caD]j`han, the following addition to web.config will be required: 8ouopai*sa^Oanran: 8d]j`hano: 8]``j]ia9Ei]caD]j`hanl]pd9&*ljcran^9CAP pula9=lnaoo*DpplD]j`han*Ei]caD]j`hannaokqn_aPula9Beha namqena=__aoo9Na]`lna?kj`epekj9ejpacn]pa`Ik`a+: 8+d]j`hano: 8+ouopai*sa^Oanran: Compile both the project containing the handler and the web project. Add the mapping in )NTERNET)NFORMATION3ERVICES-ANAGER ASSHOWNBEFORE4HEMAPPINGWILLNOWFUNCTIONPERFECTLY FORBOTHTHEDEVELOPMENTENVIRONMENTANDDIRECTUSAGEFROMTHELOCAL))3SEE4ABLE Table 3-13. Handler Settings for IIS7 Integrated Mode
Attribute
Typical Values
Description
ran^
'%4 0/34
The handler responds to the HTTP VERBSONLY/THERVERBSARE$%"5' AND(%!$
l]pd
full path or wildcards
The path that defines the requests the handlers respond to.
naokqn_aPula
File
Expect that the file exists.
o_nelpLnk_aookn
a path
0ATHTOTHEENGINE$,, THATHANDLES the request.
namqena=__aoo
3CRIPT %XECUTE .ONE 2EAD 7RITE
2EQUIREDSETTINGSFORRESOURCEACCESS
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
Attribute
Typical Values
Description
naokqn_aPula
$IRECTORY %ITHER &ILE 5NSPECIFIED
The type of the resource the handler ISMAPPEDTO4HEDEFAULTVALUEIS 5NSPECIFIED
lna?kj`epekj
See following list
Conditions that must be fulfilled to ACTIVATETHEHANDLER)FTHEREQUESTFAILS AN(440ERRORhPRECONDITIONFAILEDv is sent to the client.
]hhksL]pdEjbk
true,false
Specifies whether the handler processes full path information. If set to false, the handler processes the file information part of a path only.
ik`qhao
Names
/PTIONALCOMMASEPARATEDLISTOF modules the extension is mapped to. By default, handler settings map to -ANAGED0IPELINE(ANDLER
pula
class, assembly
The type name of the handler.
TypicalVALUESFORlna?kj`epekj are these:
s ^epjaoo/., ^epjaoo20!CTIVATE BITOR BITMODERESPECTIVELY
s nqjpeiaRanoekj-*-, nqjpeiaRanoekj.*,4HEREQUIREDRUNTIMEONTHESERVER
s _h]ooe_Ik`a, ejpacn]pa`Ik`a-ODETHAT))3ISRUNNINGIN
s i]j]ca`D]j`han2EQUIRESTHEHANDLERTOBEWRITTENINMANAGEDCODE
Configure via IIS Management Console The settings in web.configREQUIREDBY))3INTEGRATEDMODECANBEALTEREDVIATHE))3-ANAGEMENT Console. The settings correspond directly. Altering web.config will result in an immediate change to THE-ANAGEMENT#ONSOLESETTINGS ANDVICEVERSA4OCONFIGUREUSINGTHE))3-ANAGEMENT#ONSOLE 1. /PEN)NTERNET)NFORMATION3ERVICE-ANAGER 2. /PENTHEWEBYOUWANTTOCHANGE 3. )NTHE))3SECTION DOUBLE CLICKONTHE(ANDLER-APPINGS 4. #LICKON!DD-ANAGED(ANDLERINTHETASKLISTTOTHERIGHT 5. %NTERTHESEVALUESINTHEDIALOG,EAVETHEDIALOGBYCLICKING/+ THEN a. 4HE2EQUESTPATH TIME b. Choose the handler from the drop-down list. The handler will appear in the list, as long as you’re in the right web and the project compiles. c. 'IVETHEHANDLERANAPPROPRIATENAME d. #LICKON2EQUEST2ESTRICTIONS e. /PENTHE6ERBSTAB f. #LICKONONEOFTHEFOLLOWINGVERBSANDENTERTHEVALUEh'%4v 6. ,EAVETHEMAINDIALOGBYCLICKING/+
145
146
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Configure Using Generic Handlers The final optionISNOTACONFIGURATIONOPTION BUTAWAYOFINVOKINGHANDLERSWITHOUTCONFIGURING them in web.config. By default, ASP.NET defines handlers using the extension *]odt. Therefore, PLACINGTHECODEFORAHANDLERINAFILEUSINGTHEFOLLOWINGDECLARATIVEFORMISSUFFICIENTTOGETIT working. There is no further need for web.config!NADVANTAGEOFTHISISTHATYOUWONTNEEDTODISTINGUISHBETWEENTHESETTINGSFOR))3INTEGRATEDMODEANDOTHER7EB3ERVERS NORWILLYOUNEEDTO maintain settings in the web.config regarding handlers. #REATINGSUCHAHANDLERINVOLVESTWOSTEPS 1. For each handler required, create a file with the extension *]odt. 2. Add the following declaration at the top of the file: 8!adej`
Name
Name of the file containing the code.
@a^qc
True, False
Compile in debug or release mode. In debug mode, the symbol file (.pdb) is created.
S]njejcHarah
TO
7ARNINGLEVEL
?kilehanKlpekjo
@ao_nelpekj
/PTIONSFORON THE FLYCOMPILATION
A description for documentation purposes only. The PAGEPARSERDOESNOTRECOGNIZETHISATTRIBUTE
7HETHERYOUUSETHECONFIGURATIONFILEORTHE*]odt extension is a matter of preference. HowEVER THEREARESOMEBASICGUIDELINESFORSELECTINGTHEBESTOPTION5SINGTHE*]odt file within an APPLICATIONISBETTERFORSMALLPROJECTSORHANDLERSTHATHAVESIMPLE BUTSPECIFIC TASKS)FYOUPLAN TOREUSETHEHANDLERSEVERALTIMES ORINSEVERALPROJECTSONTHESERVER YOUSHOULDSEPARATEITINTOITS own assembly and register it in the Global Assembly Cache (GAC). Storing handlers in assemblies with signing and deployment capabilitiesISFORLARGERPROJECTSINVOLVINGHANDLER reuse.
Testing and Debugging Modules and Handlers (AVINGDEPLOYEDANDCONFIGUREDTHEMODULEORHANDLER DEBUGGING may be required. In Visual Studio, THENORMAL$EBUGMODEWORKSWELLFORDEBUGGINGHANDLERS9OUREPROBABLYALREADYFAMILIARWITHTHE DEBUGGINGCAPABILITIESIN6ISUAL3TUDIO3ETTINGBREAKPOINTSANDVIEWINGVARIABLESVALUESISJUSTAS simple for handlers as for any other type of .NET solution.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
Debug Using IIS You may occasionally experience trouble with your application when running on IIS. There are no breakpoints, and adding, logging, and tracing capabilities can be tedious. In large projects there are often coding guidelines that require you to add tracing code and to log pertinent messages (such as exceptions). In smaller projects, it might not be appropriate to write more code for logging and tracING3ETTINGASIMPLEBREAKPOINTANDEXAMININGAVALUEORCONDITIONDURINGAREQUESTWOULDBENICE It can be done. To do so, you can attach a debugger to a running application. In the case of an !30.%4APPLICATIONRUNNINGON))3 THISISTHEWORKERPROCESSWWPEXE,ETSCONSIDERTHECASE WHEREYOUHAVENOTPUBLISHEDYOURPROJECT BUTSIMPLYCOMPILEDONTHEFLY ANDYOURSOURCESTHE *_oFILESARESTILLAVAILABLEBESIDETHE*]olt files. (I’ll discuss techniques for attaching to a precomPILEDPROJECTLATERINTHESECTIONh0ROBLEMS$EBUGGINGTHE7ORKER0ROCESSv (ERE THE6ISUAL3TUDIO debugger attaches automatically to the current process when you hit F5 and start a debug session. 4HISCURRENTPROCESSISTHEINTERNAL7EB3ERVERINCLUDEDIN6ISUAL3TUDIO9OUCANACHIEVETHESAME thing simply by attaching the debugger to the worker process. If the worker process is not running, FORCEITBYREQUESTINGTHEFIRSTPAGEANDINVOKINGTHEMODULESORHANDLERSCONFIGUREDFORYOURAPPLIcation. It doesn’t matter whether it is running properly or not. Here is a brief summary of the pre-conditions so far:
s ))3ISCONFIGUREDTORUNTHE7EBDIRECTLYFROMPROJECTFILES
s 6ISUAL3TUDIOISRUNNINGANDHASTHEPROJECTLOADEDHOWEVER THEREISNODEBUGSESSIONSO far).
s 4HE7ORKERPROCESSISUPANDRUNNINGSEE&IGURE
NTip You can force the worker process to start by invoking a first request to the application. To check whether it is available, open Task Manager, switch to the Processes tab, check the box (Windows Server 2008) or click the button (Windows Vista) Show Processes From All Users, and search for w3wp.exe in the list. Now open Visual Studio and attach the debugger to the worker process: 1. /PENDebug ° Attach to Process . . . 2. In the subsequent dialog, check these settings: s Transport: default. s Qualifier4HENAMEOFTHESERVERORWORKSTATION s )NTHEAttached toSECTION YOUSHOULDATLEASTHAVETHEOPTION-ANAGED#ODESELECTED 5SETHE3ELECTBUTTONTOCHANGESETTINGS 3. )NTHELISTOF!VAILABLE0ROCESSES LOOKFORTHEWORKERPROCESS)FITSNOTTHERE TICKTHECHECKbox Show processes from all users4HISISSAMEOPTIONASINTHE4ASK-ANAGER 4. 5SETHE2EFRESHBUTTONTORELOADTHELISTOFPROCESSESDURINGTHESESSIONWITHOUTCLOSINGTHE dialog. 5. -ARKAVAILABLEWORKERPROCESSESANDCLICKAttach, ASSHOWNIN&IGURE
147
148
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Figure 3-13. Use the Task Manager to check for the worker process.
$EPENDINGONYOURSERVERCONDITIONS YOUMAYFINDTHATSEVERALWORKERPROCESSESAPPEARINTHE list. If you’re not sure which process is the one handling the current request, you can attach to all of THEM!LTERNATIVELY USE4ASK-ANAGERTOKILLALLTHEWORKERPROCESSES ISSUEANEWREQUESTTOYOUR APPLICATION ANDREFRESHTHELIST)FNOONEELSEISUSINGTHESERVER ONEWORKERPROCESSWILLAPPEAR /FCOURSE THEUSAGEOFWORKERPROCESSESASEXPLAINEDIN#HAPTERDEPENDSONTHETHREADS REQUIRED5NDERRARECIRCUMSTANCES THEAPPLICATIONNEEDSMOREPOWERANDSPLITSTHEREQUESTSINTO MULTIPLEWORKERPROCESSES(OWEVER ATTACHINGSEVERALINSTANCESOFTHEWORKERPROCESSTOTHESAME debugger session is quite easy.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
7ITH6ISUAL3TUDIORUNNINGINDEBUGMODE ASITISWHENYOUHITTHE&KEY YOUCANSETBREAKPOINTSWITHINTHEMODULEORHANDLERCODEANDINVOKEAREQUESTTOHITTHEBREAKPOINT9OUCANEVEN watch the debug and trace information in the output window.
Figure 3-14. Attaching the debugger to the worker process
Problems Debugging the Worker Process 3OMETIMESTHEBEHAVIOROFTHEDEBUGGERDOESNOTMATCHYOUREXPECTATIONS1UITEFREQUENTLYTHE BREAKPOINTSAPPEARINACTIVE ORTHEYCANTBEhHIT vAS6ISUAL3TUDIOCALLSITSEE&IGURE
Figure 3-15. The breakpoint will not currently be hit. 5SUALLYTHISISBECAUSETHEPAGEHASNOTYETBEENLOADED3INCEYOUDECIDEDTOLET!30.%4 COMPILEPAGESONTHEFLY THECURRENTPAGEMIGHTNOTYETBEAVAILABLEANDTHEREFORETHESYMBOLSARE not built. To check this, use the Modules dialog in Visual Studio as shown in Figure 3-16.
149
150
C H APT ER 3 N MODU L ES A ND HA NDL ER S
Figure 3-16. Use the Modules window to retrieve information about loaded symbols. )NTHE-ODULESWINDOW LOOKFORTHEASSEMBLYYOUBUILTFORYOURMODULEORHANDLER)NTHECONtext menu of each entry, use the Symbol load informationITEMTORETRIEVEMOREINFORMATION%ITHER you’ll obtain the full path, or the symbol file (*l`^) is loaded from the list of paths Visual Studio has tried so far (see Figure 3-17).
NTip To debug parts of the operating system or .NET Framework, use the Modules dialog to attach foreign pdb files. Additionally, it can be a good idea to set up a symbol server in your company in order to have common symbol files ready, or, alternatively, attach to Microsoft’s public symbol server.
Figure 3-17. Use the symbols settings in Visual Studio to optimize access to public pdbs.
C H A P T E R 3 N M O D U LE S A N D H A N D LE R S
/NCEEVERYTHINGISFUNCTIONINGNORMALLYANDTHEATTACHEDSYMBOLSAREAVAILABLE THEBREAKpoints should function as expected and be “hit” when the code execution reaches them (see Figure 3-18).
Figure 3-18. Everything is ready to go if the breakpoint is active.
Set Up Tracing for Handlers Aspx pagesHAVEAPOWERFULANDPOPULARTRACINGFEATURE7HENYOUSETTHEFOLLOWINGPAGEDIRECTIVE the page’s content will be replaced by a complete analysis of the request: 8!]oa base class. The full definition looks like this: qoejcOuopai7 qoejcOuopai*?khha_pekjo*Ola_e]heva`7 j]iaol]_aOuopai*?kjbecqn]pekj*Lnkre`an w lq^he_]^opn]_p_h]ooLnkre`an>]oa w lnkpa_pa`Lnkre`an>]oa$%7 lq^he_renpq]hopnejc@ao_nelpekjwcap7y lq^he_renpq]hopnejcJ]iawcap7y lq^he_renpq]hrke`Ejepe]heva$opnejcj]ia(J]iaR]hqa?khha_pekj_kjbec%7 y y There is nothing specific to providers here so far. The properties J]ia and @ao_nelpekj are for descriptive purposes only. J]ia is the internal name used in configuration settings, and @ao_nelpekj SUPPORTSGRAPHICALTOOLS)FTHEDESCRIPTIONISNOTSET THEPROPERTYRETURNSTHENAMEINSTEAD4HE name is mandatory within the configuration collection. lq^he_renpq]hopnejc@ao_nelpekj w cap w eb$opnejc*EoJqhhKnAilpu$pdeo*[@ao_nelpekj%% w napqnjpdeo*[@ao_nelpekj7 y napqnjpdeo*J]ia7 y y The only method to put the provider in operation is Ejepe]heva. The _kjbec parameter passes the configuration settings. The j]ia parameter is required because you can name the provider in the configuration file, while the call to Ejepe]heva transfers this name for further reference. As you can see from this code snippet, the base class does not do anything useful. This is what MAKESITHARDTODEVELOPYOUROWNPROVIDERFROMSCRATCH)FYOUWANTTOMODIFYTHEBEHAVIOR ITS much easier to develop one of the existing classes and override the properties or methods that don’t FITYOURNEEDS(OWEVER INTHISCHAPTER)LLEXPLAINHOWTOWRITEACUSTOMPROVIDERFROM scratch.
157
158
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
Making the Provider Available To make thePROVIDERAVAILABLE YOULLNEEDASERVICETHATUSESTHEPROVIDER(OWEVER THEREARENO one-to-one relationships between services and their providers. The provider model exists to enable the substitution of one provider with another. To create a custom provider, you’ll need a custom service and one or more custom providers. The service itself does not have a base class, but it does hold several references to other types discussed in this section:
s 4HECOLLECTIONOFCONFIGUREDPROVIDERS
s 4HEPARTICULARPROVIDERCURRENTLYASSOCIATEDWITHTHESERVICE
s 4HECONFIGURATIONPARAMETERSASSIGNEDTOTHECURRENTPROVIDER
s 4HECODETOINSTANTIATEANDINITIALIZETHEPROVIDER
Somewhere in your application, there will be a consumer of this service. Consider the service from the perspective of this consumer. The consumer code needs the service in order to obtain data or do something useful. The consumer is not concerned with how the service is configured or how it stores or retrieves data. Even the service itself does not care about this, but handles it using a custom definition of the provider. With both of these abstraction tiers, it’s possible to replace the provider without altering anything within either the service or its consumers.
Configuring the Provider A providerSERVESASALAYERBETWEENANUPPER LEVELTIERANDADATATIER)TCANBESIMPLEORCOMplex, but there’s nothing specific to providers except for the Ejepe]heva method. The uniqueness of providers is in the special way you can configure and attach them using the web.config file. This MAKESAPROVIDERACCESSIBLETOTHOSEWHOCONFIGUREANDMAINTAINA7EB3ERVER)NCIDENTALLY THE namespace for the provider’s base class indicates that providers are all about configuration.) All provider configurations follow the following pattern: 8_kjbecqn]pekj: 8ouopai*sa^: 8oanre_aJ]ia: 8lnkre`ano: 8_ha]n+: 8]``j]ia9iuLnkre`an+: 8+lnkre`ano: 8+oanre_aJ]ia: 8+ouopai*sa^: 8+_kjbecqn]pekj: Notice that there is not one provider but a collection of them. Usually there will be only one, but the configuration schema allows you to configure several. The configuration classes, as explained later in the section “Extending the Configuration,” manage the typical pattern in the XML file based on the instructions 8_ha]n+:, 8naikra:, and 8]``:. This is necessary because the web.config files form a hierarchy from machine level down to the specific subfolder in the application. As long as you have permission, you can override the inherited settings, remove one or all entries, and add your own. Since you have to handle a collection of providers even if only one is needed, a collection class is required. As for the provider’s base class, there is a base class for collections, too. j]iaol]_aOuopai*?kjbecqn]pekj*Lnkre`an w lq^he__h]ooLnkre`an?khha_pekj6E?khha_pekj(EAjqian]^ha w lq^he_Lnkre`an?khha_pekj$%7
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
lq^he_^kkhEoOuj_dnkjeva`wcap7y lq^he_k^fa_pOuj_Nkkpwcap7y lq^he_Lnkre`an>]oapdeoWopnejcj]iaYwcap7y lq^he_renpq]hrke`=``$Lnkre`an>]oalnkre`an%7 lq^he_rke`?kluPk$Lnkre`an>]oaWY]nn]u(ejpej`at%7 lq^he_EAjqian]pknCapAjqian]pkn$%7 lq^he_rke`Naikra$opnejcj]ia%7 lq^he_rke`OapNa]`Kjhu$%7 y y Using the Lnkre`an>]oa type, it’s easy to handle the custom provider as well as any of the default built-in ones using this collection. You can also specify one of the providers to be the default provider. This links the service to a provider in the absence of any other selection criteria. So far, you have a service supplying the consumers, a provider as a late-bound component that hides the details of the data or communication tier, and finally a storage location for a collection of PROVIDERDEFINITIONS(OWEVER THEFORMATOFTHECONFIGURATIONALSOREQUIRESASECTIONDEFINITION Configuration sections are defined at the beginning of the web.config file and extend the conFIGURATIONBYSPECIFYINGWHERETHEACTUALDEFINITIONSARESTORED)NITIALLY )ASSUMEDTHATPROVIDER configurations are stored in the 8_kjbecqn]pekj:8ouopai*sa^: hive. This is the default setting for BUILT INPROVIDERSBUTNOTCOMPULSORYFORCUSTOMPROVIDERS)NFACT YOUCANUSEANYPATHWITHIN the 8_kjbecqn]pekj:TOP LEVELELEMENT)TSGOODPRACTICETOSTAYWITHTHISMODELANDPLACECUSTOM providers there, too. The configuration sections are defined within the 8_kjbecOa_pekjo: element. Beneath this element, you’ll find a hierarchy of 8oa_pekjCnkql: and 8oa_pekj: elements in no particular order. The hierarchy of configuration elements is the exact representation of the sections used below the 8_kjbecqn]pekj: part in the web.config(OWEVER THEDEFAULTweb.config has more sections than the 8_kjbecOa_pekj: section does. The reason is that your current web.config inherits from the global, machine-wide definition. You can find the configuration file that contains all possible section definitions within the following path: !ouopai!XIe_nkokbp*JAPXBn]iaskngXr.*,*1,3.3X?KJBECXi]_deja*_kjbec To add your own configuration section to support a specific location for your custom provider definition, you’ll need to add a section definition. First, a custom section definition class has to be implemented. The following base class is the final step in obtaining a complete definition for custom providers: j]iaol]_aOuopai*?kjbecqn]pekj w lq^he_]^opn]_p_h]oo?kjbecqn]pekjOa_pekj6?kjbecqn]pekjAhaiajp w lnkpa_pa`?kjbecqn]pekjOa_pekj$%7 lq^he_Oa_pekjEjbkni]pekjOa_pekjEjbkni]pekjwcap7y lnkpa_pa`ejpanj]hrenpq]hrke`@aoane]hevaOa_pekj$TihNa]`anna]`an%7 lnkpa_pa`ejpanj]hrenpq]hk^fa_pCapNqjpeiaK^fa_p$%7 lnkpa_pa`ejpanj]hkranne`a^kkhEoIk`ebea`$%7 lnkpa_pa`ejpanj]hkranne`arke`NaoapIk`ebea`$%7 lnkpa_pa`ejpanj]hrenpq]hopnejcOane]hevaOa_pekj$ ?kjbecqn]pekjAhaiajpl]najpAhaiajp( opnejcj]ia( ?kjbecqn]pekjO]raIk`ao]raIk`a%7 y y
159
160
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
From this declaration, you can see that ?kjbecqn]pekjOa_pekj in turn inherits from the ?kjbecqn]pekjAhaiajp class. This is the reason why you can mix 8oa_pekjCnkql: and 8oa_pekj: elements easily. The class contains several implemented methods and others that you must override to obtain the required behavior. The Oane]hev]pekjOa_pekj and @aoane]hev]pekjOa_pekj methods convert the settings to and from XML.
General Considerations All providers have certain characteristics in common. The base class, as already explained, forces few typical usage scenarios only. You should know some of the best practices for creating providers that fit well into the framework under all circumstances.
Initialization Procedure of a Provider All providers derive from Lnkre`an>]oa. Therefore, they inherit the Ejepe]heva method, which is declared renpq]h)N# THISINDICATESAMETHODTHATYOUNEEDTOOVERRIDE$OINGSOTRANSFERSAFEW critical tasks over to you. Most providers need specific permissions to run. Requesting permissions is the way you let the runtime know what your code needs to be allowed to do. This is required by access to a SQL database or local file system, for instance. The best way to implement permission checks is to add the appropriate attributes from the Ouopai*Oa_qnepu*LanieooekjoNAMESPACE)MAGINETHATAPROVIDER ISAPLUGGABLEMODULETHATOTHERSMAYREUSE EVENIFITDOESNOTOPERATEWELL)NSTEADOFEVENTUALLY FAILINGINCUSTOMCODE THEINITIALIZATIONPROCEDURESHOULDIMMEDIATELYREPORTANEXCEPTIONINDICATing what went wrong. This is precisely the purpose of such permission attributes. Next, the _kjbec parameter passed to the Ejepe]hevaMETHODISIMPORTANT)FITISNULL ITMEANS THATSOMETHINGWENTWRONGWITHINTHECONFIGURATION)NTHATCASE THROWAN=ncqiajpJqhhAt_alpekj, even if there are no configuration parameters required. The error indicates an unexpected condition and must be reported to the calling method. The base class’s Ejepe]heva methodISNOTABSTRACT)TCONTAINSCODEANDMUSTBECALLEDTO ensure that basic requirements are met. As shown in the last section, the code validates the name and description settings. Even if you intend to change this, and you replace the name and descripTIONVALUESINYOURCODE CALLTHEBASEMETHOD)FAFUTUREVERSIONOFTHE.%4FRAMEWORKADDSCODE here to ensure required functionality, then failing to call the base class could break your provider. 2EADINGTHECONFIGURATIONISANIMPORTANTPART(OWEVER TEXTUALDEFINITIONS EVENIFMADEIN XML, can be error-prone. Use the J]iaR]hqa?khha_pekj class and its indexer to access the configuRATIONVALUES)FAREQUIREDPARAMETERISMISSING THROWALnkre`anAt_alpekj. Make sure you call Naikra each time you read a parameter. That clears the _kjbec objects one by one. After all required parameters are processed, nothing should be left in the _kjbecOBJECT)F_kjbec*?kqjp:, is still pnqa, throw a Lnkre`anAt_alpekj. This ensures that you have all the required parameters and that the user has no other elements in the configuration that can’t be processed. You might wonder why you can’t just ignore these values. This is because the user’s intention was probably not to add priVATEVALUESTOSTOREADDITIONALINFORMATIONTHERE(EORSHEHASPROBABLYMISTYPEDANATTRIBUTE4HE EXCEPTIONHELPSTORECOGNIZETHESETYPOSINSTEADOFSEARCHINGFORTHEERRORINEVENTLOGS There are also best practices regarding provider code. First, the service and the provider are two different things. Even if you develop them together in one project, with nothing else in mind, they should stay independent. Second, never call a method of the service from the provider. This could lead to infinite, recursive loops, or a break in modularity. Exchanging the provider would be impossible because it is tightly bound to the service.
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
Lifetime Providers are loaded dynamically the first time the service requires them. This ensures that PROVIDERSARENOTHELDINMEMORYWHENTHEYARENOTNEEDED(OWEVER LOADINGACOMPLEXPROVIDER could be a time-consuming task—clearly not desirable when you’re trying to improve the responsiveness of a site. You can force the provider to load by calling certain methods of the services in the =llhe_]pekj[Op]np event. This assumes that the provider has a lifetime equaling the lifetime of the application and that the provider has a global state. A provider should not depend on the current session or the context. (OWEVER Dppl?kjpatp and all subsequent classes can be used at any point to retrieve data. This MAKESITSAFETOUSEPRIVATEFIELDSTOSTOREVALUES)TSAVESMEMORYANDSPEEDSUPTHEPROVIDER(OWever, you must remember to write thread-safe code.
Thread Safety Threads in ASP.NET are explained in depth in Chapter 2. Web applications are multi-user applications—as with mainframe computer software, you can consider web applications as massively parallel applications. Consequently, when dealing with providers, you must handle threads with care. Providers are instantiated only once and shared across all requests. This speeds them up and causes them to consume less memory, which is necessary for basic tasks. Since threads run in parallel—that’s what threads are for—one provider might be called at the same time by multiple threads. This means that you must always code a provider to be thread-safe. Otherwise, you risk throwing exceptions or creating garbage on any request. There are subtle differences that might appear under heavy workload only. The only exception is the Ejepe]heva method. Since it’s only called once, thread safety isn’t an ISSUE!SLONGASTHESERVICEINITIALIZESIN=llhe_]pekj[Op]np THISISALWAYSTRUE)FYOUCANNOTBESURE of this, the code that calls the method in the service has to be locked, as shown in the following code snippet: eb$[lnkre`an99jqhh% w hk_g$[hk_g% w eb$[lnkre`an99jqhh% w This ensures that the provider is not loaded twice. The hk_g statement blocks other threads. (OWEVER ANOTHERTHREADMIGHTHAVEPASSEDTHELOCKANDALREADYBERUNNINGINSIDETHEMETHOD That’s why you must check the existence of the provider again after getting the lock. For property access, a similar technique is appropriate. Usually, you write your properties like this: lner]paQjep[oeva7 lq^he_QjepOeva w capwnapqnj[oeva7y oapw[oeva9r]hqa7y y
161
162
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
)MAGINETHATTWOTHREADSACCESSTHISMETHOD4HEREISONLYONEINSTANCEOFTHISCLASSINTHE MEMORY ANDHENCEONESTORAGEMEMORYPLACE FORTHEVALUE)FONETHREADWRITESTHEVALUEAND the other reads the value at the same time, you’ll receive an incorrect value back. The hk_g statementISAGAINTHESOLUTION)TSASHORTCUTTOTHEOuopai*Pdna]`ejc*Ikjepkn class of the framework the compiler creates for us. The code should look like the following snippet: lner]paQjep[oeva7 lner]pak^fa_p[ouj_d lq^he_QjepOeva w capwhk_g$[ouj_d%wnapqnj[oeva7yy oapwhk_g$[ouj_d%w[oeva9r]hqa7yy y The Ikjepkn methodISAVERYBASICLOCK)TBLOCKSALLTHREADS EVENIFTHEYAREBEINGACCESSEDFOR reading only. This could slow down an application under heavy load. 4HEREARESEVERALOTHERWAYSTOOPTIMIZETHEBEHAVIOR4HENa]`anSnepanHk_g class improves behavior by allowing shared reads but preventing overlapping write and read access to the property. This improves performance if the values are mostly read rather than written. Another method uses the Ouopai*Nqjpeia*?kilehanOanre_ao*Iapdk`Eilh=ppne^qpa. See the following code for a usage scenario: lner]paQjep[oeva7 WIapdk`Eilh$Iapdk`EilhKlpekjo*Ouj_dnkjeva`%Y lq^he_QjepCapOeva$% w napqnj[oeva7 y WIapdk`Eilh$Iapdk`EilhKlpekjo*Ouj_dnkjeva`%Y lq^he_rke`OapOeva$Qjepoeva% w [oeva9oeva7 y (OWEVER THISREPLACESPROPERTIESWITHMETHODS WHICHISNTTHEBESTCODINGSTYLE4HELOCKING experience isn’t any better. The compiler also uses the Ikjepkn method. As you can see from the code snippet, there is no object for storing the locking state. The compiler selects an object at the best level, like hk_g$pdeo%. This could be an object on either the type or application level, which is a higher level than within the method. Usually this means that the locking phase is enduring, and this causes the threads to last longer. Unless locking on type level is required, this attribute is not appropriate for providers. Which method is best will depend on the specific conditions. Generally, you have to ensure THREAD SAFEACCESSTOALLINSTANCEDATA INCLUDINGPRIVATEFIELDS(OWEVER WHENYOUWRITECONfiguration data in the Ejepe]heva method (thread-safe because it’s only called once) and provide read-only access, no action is required to ensure thread safety. The cap statement of the property shown in the last code snippets might omit the lock statement, then. Calls to access all stack-based data and local variables within a property or method should not be locked.
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
Creating a Custom Provider-Based Service )MAGESARE an essential part of almost all web applications. Powerful image management makes YOURLIFEANDYOURUSERSLIVESEASIER)MAGINETHATYOURFINALSTORAGESOLUTIONISNTCLEARLYDEFINED SO you want to create a flexible and extensible image management solution. Other developers in your ORGANIZATIONSHOULDBEABLETOADOPTYOURCODEANDREPLACEPARTSOFITTOSUITTHEIRNEEDSWITHOUT KNOWINGTHEINTERNALDETAILSOFTHEIMAGECREATIONANDDELIVERYPROCESS)NSUCHASITUATION YOU could consider a custom provider-based service. Again, the multi-tier model points to a solution: 1. You need to create a service that is able to retrieve and send image data. 2. You need a provider interface that makes data storage access replaceable. 3. You need a specific provider to put it into operation. 4. You need a configuration definition to handle the configuration in web.config. Following the model of the provider as previously described in the section “The Anatomy of a Provider,” you should create these classes:
s !NIMPLEMENTATIONOFACONFIGURATIONSECTIONDEFINITIONCLASS
s !NABSTRACTBASECLASSTHATDESCRIBESTHEPROVIDER
s !NIMPLEMENTATIONOFTHISBASECLASSTHATCREATESTHEPROVIDERTHATSERVESASACONCRETE implementation against data storage
s !CLASSTHATIMPLEMENTSTHESERVICETODOANYTHINGUSEFULUSINGTHEPROVIDER
s !PROVIDERCOLLECTIONCLASSTHATSTORESMULTIPLEPROVIDERS
!DDITIONALLY DATASTORAGEMUSTBEPROVIDED"ECAUSEITSCOMMONTOUSE31,3ERVER )LLCREATE the image service’s default provider using a SQL Server database. You can build such a solution from the bottom up, beginning with the configuration, followed by the provider and finally the service. This is fine if you have a design and planning phase in your PROJECTANDCLEARLYDEFINEDREQUIREMENTS(OWEVER FORLEARNINGPURPOSES YOULLCREATEAPROVIDER from the top down and start with the last step—the service. This clarifies the purpose of the provider and makes the next steps easier to understand.
Limitations of the Code Samples Depending on real-life requirements, creating a provider-based solution might require a little more effort. Remember that, for the sake of clarity, all the code snippets here lack error handling, UNITTESTING ANDLOGGINGFEATURES)NADDITION NOCODESNIPPETSHEREFEATURELOCALIZATION4HEBEST PRACTICEISTOSTORERESOURCESINRESOURCEFILESANDTOLOCALIZEIFTHEAPPLICATIONISUSEDINMULTIPLE countries. See Chapter 5 for more information about resources. #ERTAINSAMPLES LIKEINTHESECTIONh)MPLEMENTINGAN%XPRESSION"UILDERWITH$ESIGN 4IME3UPPORTvINTHISCHAPTER ACCESSDATABASES)FYOUAREWRITINGMORESOPHISTICATEDPROVIDERS YOUMAYHAVE multiple database operations for one action. When the database supports transactions, use transactions to ensure the atomicity of updates. Transactions ensure a rollback if one of the database operations fails. )FTHEDATABASEDOESNOTINHERENTLYSUPPORTTRANSACTIONS YOUMUSTENSURETHEATOMICITYWITHINYOUROWN code. Add pnu/_]p_d blocks and check conditions to the code shown here. Some sample code regarding providers in this and all remaining chapters shows only one part of the provider to explain specific techniques. Any methods or properties that are not implemented HAVEBEENOMITTEDFORTHESAKEOFCLARITYANDSIMPLICITY(OWEVER FORPRODUCTIONCODEYOUSHOULD add a JkpEilhaiajpa`At_alpekj to redundant methods. This verifies that users of the provider only IMPLEMENTTHEFEATURESYOUINTENDED)FCOMPLETENESSISNOTREQUIRED DONTFORGETTOEXPLAINTHISIN the documentation as well.
163
164
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
Creating a Service The purpose of the sample service is to retrieve an image via the NapnearaEi]ca method. All the other service properties and methods are required to set up the provider. The provider allows other developers using the service to replace the default provider with their own version. For instance, you could choose to use the file system on a file server to store images instead of the database. No change would be required in the application or the service implementation. Let’s take a look at the class itself, as shown in Listing 4-1. Listing 4-1. The Service Itself Is Able to Retrieve an Image qoejcOuopai*Sa^*?kjbecqn]pekj7 qoejcOuopai*?kjbecqn]pekj7 qoejcOuopai*?kjbecqn]pekj*Lnkre`an7 qoejcOuopai*@n]sejc7 j]iaol]_a=lnaoo*Atpajoe^ehepu*?qopkiLnkre`an w lq^he__h]ooEi]caOanre_a w lner]paop]pe_Ei]caLnkre`an[lnkre`an9jqhh7 lner]paop]pe_Ei]caLnkre`an?khha_pekj[lnkre`ano9jqhh7 lner]paop]pe_k^fa_p[hk_g9jask^fa_p$%7 lq^he_Ei]caLnkre`anLnkre`an w capwnapqnj[lnkre`an7y y lq^he_Ei]caLnkre`an?khha_pekjLnkre`ano w capwnapqnj[lnkre`ano7y y lq^he_op]pe_Ei]caNapnearaEi]ca$ejpei]caE@% w Hk]`Lnkre`ano$%7 napqnj[lnkre`an*NapnearaEi]ca$ei]caE@%7 y lner]paop]pe_rke`Hk]`Lnkre`ano$% w eb$[lnkre`an99jqhh% w hk_g$[hk_g% w eb$[lnkre`an99jqhh% w k^fa_pWY]ppne^qpao9± pulakb$Ei]caLnkre`anOa_pekj%*Cap?qopki=ppne^qpao$± pulakb$Oa_pekj=ppne^qpa%(b]hoa%7 eb$]ppne^qpao*Hajcpd9-% pdnksjas?kjbecqn]pekjAt_alpekj$Oa_pekj=ppne^qpajkpoap%7 Oa_pekj=ppne^qpao]9$Oa_pekj=ppne^qpa%]ppne^qpaoW,Y7 Ei]caLnkre`anOa_pekjoa_pekj9± $Ei]caLnkre`anOa_pekj%± Sa^?kjbecqn]pekjI]j]can*CapOa_pekj$o]*Oa_pekjJ]ia%7
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
[lnkre`ano9jasEi]caLnkre`an?khha_pekj$%7 Lnkre`anoDahlan*Ejop]jpe]paLnkre`ano$oa_pekj*Lnkre`ano(± [lnkre`ano(± pulakb$Ei]caLnkre`an%%7 [lnkre`an9[lnkre`anoWoa_pekj*@ab]qhpLnkre`anY7 eb$[lnkre`an99jqhh% pdnksjasLnkre`anAt_alpekj$Qj]^hapkhk]``ab]qhpEi]caLnkre`an%7 y y y y y y The NapnearaEi]ca method is static, because you don’t need to handle multiple instances of the service. Although the configuration allows multiple providers, only one can act as the current proVIDER4HEOTHERPARTSOFTHECODEINTERACTWITHTHISCURRENTPROVIDER)NTHISAPPLICATION THESERVICE class is created once and starts working when the Hk]`Lnkre`ano method is called. Each method launches with a call to the Hk]`Lnkre`ano method to ensure that a provider is present when retrieving data. Loading the provider is a one-time operation. To avoid claiming the lock if providers are already loaded, the first action is checking the _provider variable. After claiming the lock, this test is made again to ensure the provider is still not loaded. This is required, as parallel running requests might invoke the service and start a duplicate procedure. Changing the _provider variable afterwards could change the conditions, and you’re supposed to recheck the state again. )NTHENEXTSTEP THENAMEOFTHESECTIONISRETRIEVEDBYREADINGAPRIVATEATTRIBUTE4HEREARE several ways to do this. Using an attribute allows a single location definition. The section configuration, which provides the necessary information, is the only class where the definition is used. Using data from the Oa_pekj=ppne^qpa attribute, it’s possible to get a reference to the 8ei]caOanre_a: section you use to define the current provider. Then the code loads all the registered providers and points _provider to the default provider to make it the current one. Lnkre`anDahlan is a static class in the Ouopai*Sa^*?kjbecqn]pekj namespace that simplifies the process. )FANYTHINGGOESWRONG ANEXCEPTIONISRAISEDTOINFORMTHECALLINGCODE)FEVERYTHINGISFINE the Ei]caOanre_a class will be ready to serve images.
Creating the Provider The provider consists of two classes. One is an abstract base class that extends the Lnkre`an>]oa class with the required additional methods. The other is the implementation of that class. Again, this provider is just one implementation to bring the whole solution into operation. Other developers could replace this provider with their own creation and extend the behavior of the service at will (Listing 4-2). Listing 4-2. Base Class for the Provider qoejcOuopai*?kjbecqn]pekj*Lnkre`an7 qoejcOuopai*@n]sejc7 j]iaol]_a=lnaoo*Atpajoe^ehepu*?qopkiLnkre`an w lq^he_]^opn]_p_h]ooEi]caLnkre`an6Lnkre`an>]oa w lq^he_]^opn]_popnejc=llhe_]pekjJ]iawcap7oap7y lq^he_]^opn]_pEi]caNapnearaEi]ca$ejpe`%7 y y
165
166
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
4HISCLASSHASTWOCHARACTERISTICS)TMUSTINHERITTHELnkre`an>]oaINORDERTOBERECOGNIZEDAS APROVIDERINOTHERPARTSOFTHEAPPLICATION)TMUSTALSODEFINETHEMETHODSTHATSERVETHESERVICE)N this case, NapnearaEi]ca is such a method. The service knows that it can call this method to get an image, and the provider completes the process. Now the implementation is required. Since you want to pull the images from a database, some preparation are needed. Assuming you have local database, aspnetdb, with integrated security, add the table shown in Figure 4-2.
Figure 4-2. The table that stores the images (see Listing 4-2 for corresponding T-SQL script) )NTERNALLY YOUUSE,).1TO31,TORETRIEVETHEIMAGEINFORMATIONFROMTHESPECIFIEDTABLE4HE TABLEDOESNOTACTUALLYCONTAINTHEIMAGES)TCONTAINSPATHSTOTHEIMAGEFOLDERSANDALLOWSFURTHER management of the relationship between resources requesting the images. The data storage method LEADSTOTHEFIRSTREQUIREMENTOFTHEPROVIDERITMUSTSTOREACONNECTIONSTRING)TSAGOODIDEATO use the predefined 8_kjja_pekjOpnejc: section in web.config and only handle the name that references it. Next, look into the provider code (see Listing 4-3). Listing 4-3. Implementation of the Provider qoejcOuopai7 qoejcOuopai*?khha_pekjo*Ola_e]heva`7 qoejcOuopai*?kjbecqn]pekj*Lnkre`an7 qoejcOuopai*@]p]*Omh?heajp7 qoejcOuopai*@n]sejc7 qoejcOuopai*Hejm7 qoejcOuopai*Oa_qnepu*Lanieooekjo7 qoejcOuopai*Sa^7 qoejcOuopai*Sa^*?kjbecqn]pekj7 j]iaol]_a=lnaoo*Atpajoe^ehepu*?qopkiLnkre`an w WOmh?heajpLanieooekj$Oa_qnepu=_pekj*@ai]j`(Qjnaopne_pa`9pnqa%Y lq^he__h]ooOmhEi]caLnkre`an6Ei]caLnkre`an w lq^he_kranne`aopnejc=llhe_]pekjJ]ia w cap7 oap7 y lq^he_opnejc?kjja_pekjOpnejc
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
w cap7 oap7 y lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(J]iaR]hqa?khha_pekj_kjbec% w eb$_kjbec99jqhh% pdnksjas=ncqiajpJqhhAt_alpekj$_kjbec%7 eb$Opnejc*EoJqhhKnAilpu$j]ia%% j]ia9OmhEi]caLnkre`an7 eb$opnejc*EoJqhhKnAilpu$_kjbecW`ao_nelpekjY%% w _kjbec*Naikra$`ao_nelpekj%7 _kjbec*=``$`ao_nelpekj( OMHei]calnkre`an%7 y ^]oa*Ejepe]heva$j]ia(_kjbec%7 =llhe_]pekjJ]ia9_kjbecW]llhe_]pekjJ]iaY7 eb$opnejc*EoJqhhKnAilpu$=llhe_]pekjJ]ia%% =llhe_]pekjJ]ia9+7 _kjbec*Naikra$]llhe_]pekjJ]ia%7 opnejc_kjja_p9_kjbecW_kjja_pekjOpnejcJ]iaY7 eb$Opnejc*EoJqhhKnAilpu$_kjja_p%% pdnksjasLnkre`anAt_alpekj $Ailpuknieooejc_kjja_pekjOpnejcJ]ia%7 _kjbec*Naikra$_kjja_pekjOpnejcJ]ia%7 eb$Sa^?kjbecqn]pekjI]j]can*?kjja_pekjOpnejcoW_kjja_pY99jqhh% pdnksjasLnkre`anAt_alpekj$Ieooejc_kjja_pekjOpnejcJ]ia%7 ?kjja_pekjOpnejc9Sa^?kjbecqn]pekjI]j]can*± ?kjja_pekjOpnejcoW_kjja_pY*?kjja_pekjOpnejc7 eb$Opnejc*EoJqhhKnAilpu$?kjja_pekjOpnejc%% pdnksjasLnkre`anAt_alpekj$Ailpu_kjja_pekjopnejc%7 eb$_kjbec*?kqjp:,% w opnejc]ppn9_kjbec*=hhGauo*Benop$%7 eb$Opnejc*EoJqhhKnAilpu$]ppn%% pdnksjasLnkre`anAt_alpekj $Qjna_kcjeva`]ppne^qpa6']ppn%7 y y lq^he_kranne`aEi]caNapnearaEi]ca$ejpe`% w Ei]ca@]p]@]p]?kjpatp_pt9jas± Ei]ca@]p]@]p]?kjpatp$?kjja_pekjOpnejc%7 r]nmn9bnkieej_pt*]oljap[?kjbecqn]pekjo sdanae*_bc[_]packnu99Ei]caLnkre`an± ""e*_bc[e`99e`± ""e*_bc[pula99ei]ca oaha_pe*_bc[_kjpajp7 opnejc`]p]9mn*BenopKn@ab]qhp8opnejc:$%7 Ei]caeic9Ei]ca*BnkiBeha$Dppl?kjpatp*?qnnajp*Oanran*I]lL]pd$`]p]%%7 napqnjeic7 y y y
167
168
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
The life of a provider begins with the call to its Ejepe]heva method. The first step is to verify that _kjbec is not jqhh)FSO ANEXCEPTIONISTHROWN2EMEMBERTHATCONFIGURATIONISESSENTIALFORTHE provider model. As other parts of the configuration might reference the provider, it needs a name. !SSIGNTHEPROVIDERADEFAULTNAMEIFITDOESNTHAVEONE)NTHEEXAMPLE ITSCALLEDh3QL)MAGE0ROVIDERv)TSTHESAMEFORTHEDESCRIPTION%VENIFTHEDESCRIPTIONISOPTIONAL ITSHOULDBESETPROPERLY 3EVERALGRAPHICALTOOLSMIGHTREFERTOTHEDESCRIPTION)FTHEATTRIBUTEhDESCRIPTIONvDOESNTEXIST THE code creates one. Next, the base class’s Ejepe]heva method is called. The provider retrieves the ]llhe_]pekjJ]ia and _kjja_pekjOpnejcJ]iaATTRIBUTESFROMTHECONFIGURATIONFILE)FTHE]llhe_]pekjJ]ia doesn’t exist, assume it is the root application “/”. The connection string is mandatory, and the lack of it throws an exception. Now, the provider instance should have all the information required to operate. The code checks for the remaining configuration attributes. As explained in the best practice section “General Considerations,” the check—eb$_kjbec*?kqjp:,%ENSURESTHATUNRECOGNIZED attributes are reported to the user in order to avoid typos. Next, the NapnearaEi]ca method must be implemented. The Ei]ca@]p]@]p]?kjpatp is a class CREATEDWITHTHE,).1TO31,WIZARD4OCREATETHISCLASS FOLLOWTHESESTEPS 1. )N6ISUAL3TUDIO CHOOSEAdd ° New Item in the context menu of the solution. 2. )NTHESECTIONData of the Add New Item dialog choose LINQ to SQL Classes item. 3. Give the item a common name, such as ImageData.dbml (see Figure 4-3).
Figure 4-3. Add a LINQ to SQL item to the provider project. #LOSETHEDIALOGANDANEMPTYDESIGNERSURFACEWILLAPPEAR/PENTHE3ERVER%XPLORER6IEW° Server Explorer, or press Ctrl+Alt+S instead) and add a connection to your database. Either it will already be present or you’ll have to add the database and the appropriate tables. The full script for the table is shown in Listing 4-4. Listing 4-4. SQL Table Definition for the Provider Project ?NA=PAP=>HAW`^kY*W]oljap[?kjbecqn]pekjY$ W_bc[e`YWejpYE@AJPEPU$-(-%JKPJQHH( W_bc[gauYWr]n_d]nY$1,%JKPJQHH( W_bc[_]packnuYWr]n_d]nY$1,%JKPJQHH(
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
W_bc[pulaYWr]n_d]nY$-,%JKPJQHH( W_bc[_kjpajpYWr]n_d]nY$i]t%JQHH( ?KJOPN=EJPWLG[]oljap[?kjbecqn]pekjYLNEI=NUGAU?HQOPANA@ $ W_bc[e`Y=O? %SEPD$L=@[EJ@AT9KBB(± OP=PEOPE?O[JKNA?KILQPA9KBB(± ECJKNA[@QL[GAU9KBB(± =HHKS[NKS[HK?GO9KJ(± =HHKS[L=CA[HK?GO9KJ %KJWLNEI=NUY This is just a simple example of a storage method. Real-life projects tend to be more sophisticated. Once the table exists, and the Server Explorer shows the server, you can add a connection. Choose the icon Data Connections and Add Connection from the context menu; then, in the followINGDIALOG CHOOSETHESERVER)FYOUUSEALOCAL31,%XPRESS%DITION THENAMEMIGHTLOOKLIKEh SQLEXPRESS”. Select the database where you have created the table. Test the connection and close the dialog to add the connection, as shown in Figure 4-4.
Figure 4-4. Adding a connection to the current project
169
170
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
9OUCANNOWDRAGANDDROPTHETABLEONTOTHEDESIGNERSURFACEOFTHE,).1TO31,CLASS The result should look like the image already shown in Figure 4-2. Based on the class’s name, THEDESIGNERWILLCREATEACONTEXTFILE)FTHENAMEISImageData, the data context is called ImageDataDataContext (DataContext is the suffix). The context contains a property called ]oljap[ ?kjbecqn]pekjo WHICHREPRESENTSTHETABLE)FYOUHAVENAMEDTHETABLEDIFFERENTLY THEPROPERTY WILLHAVETHATNAME5SINGTHECONTEXT YOUCANUSESIMPLE,).1STATEMENTSTOQUERYTHEDATABASE as shown in the NapnearaEi]ca method. )NTHEEXAMPLE THEIMAGEISRETRIEVEDBASEDONITS)D THECATEGORYh)MAGE0ROVIDER vANDTHE type “image.” This is only a suggestion. Based on the retrieved name, the Oanran*I]lL]pd method is used to obtain the full path to the image. The Ei]ca*BnkiBeha creates the image in memory for further processing. )MAGINEANOTHERPROVIDERTHATSIMPLYTAKESTHEIMAGEANDCREATESATHUMBNAILOFIT)N a derived class, you could override the NapnearaEi]ca method, call the base class, get the image, and manipulate it to create a thumbnail. The configuration is already able to change the provider. This shows once more the power of the provider architecture.
Configuring Providers The next step is to implement the configuration support. Because many providers can serve a single service, a collection is appropriate. Listing 4-5 shows a simple implementation based on the abstract base class, Lnkre`an?khha_pekj. Listing 4-5. Provider Collections Represent All Configured Providers qoejcOuopai7 qoejcOuopai*?kjbecqn]pekj*Lnkre`an7 j]iaol]_a=lnaoo*Atpajoe^ehepu*?qopkiLnkre`an w lq^he__h]ooEi]caLnkre`an?khha_pekj6Lnkre`an?khha_pekj w lq^he_Ei]caLnkre`anpdeoWopnejcj]iaY w cap w napqnj^]oaWj]iaY]oEi]caLnkre`an7 y y lq^he_kranne`arke`=``$Lnkre`an>]oalnkre`an% w eb$lnkre`an99jqhh% pdnksjas=ncqiajpJqhhAt_alpekj$lnkre`an%7 eb$$lnkre`aneoEi]caLnkre`an%% pdnksjas=ncqiajpAt_alpekj $Ejr]he`lnkre`anpula(lnkre`an%7 ^]oa*=``$lnkre`an%7 y y y
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
There are only two features. You can add providers of the Ei]caLnkre`an type and retrieve them using an indexer. The base class handles all the other features. The method and property previously shown ensure the integrity of the base class type. This means that you won’t be able to add a provider to the configuration section that does not serve the service. The last step required in order to operate the service is to define the configuration section, as SHOWNIN,ISTING Listing 4-6. The Configuration Section and the Supporting Custom Attribute qoejcOuopai7 qoejcOuopai*?kjbecqn]pekj7 j]iaol]_a=lnaoo*Atpajoe^ehepu*?qopkiLnkre`an w W=ppne^qpaQo]ca$=ppne^qpaP]ncapo*?h]oo%Y lq^he__h]ooOa_pekj=ppne^qpa6=ppne^qpa w lq^he_Oa_pekj=ppne^qpa$opnejcoa_pekjJ]ia% 6^]oa$% w Oa_pekjJ]ia9oa_pekjJ]ia7 y lq^he_opnejcOa_pekjJ]ia w cap7 oap7 y y WOa_pekj$ouopai*sa^+ei]caOanre_a%Y lq^he__h]ooEi]caLnkre`anOa_pekj6?kjbecqn]pekjOa_pekj w W?kjbecqn]pekjLnklanpu$lnkre`ano%Y lq^he_Lnkre`anOappejco?khha_pekjLnkre`ano w capwnapqnj$Lnkre`anOappejco?khha_pekj%^]oaWlnkre`anoY7y y WOpnejcR]he`]pkn$IejHajcpd9-%Y W?kjbecqn]pekjLnklanpu$`ab]qhpLnkre`an(± @ab]qhpR]hqa9OmhEi]caLnkre`an%Y lq^he_opnejc@ab]qhpLnkre`an w capwnapqnj$opnejc%^]oaW`ab]qhpLnkre`anY7y oapw^]oaW`ab]qhpLnkre`anY9r]hqa7y y y y
171
172
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
As suggested before, a custom attribute helps to get a single location definition of the configuration path—like “system.web/imageService” in this example. The custom Oa_pekj=ppne^qpa attributeDOESNOTINVOLVEANYTHINGSPECIAL)TISUSEDTODECORATETHEEi]caLnkre`anOa_pekj class, which contains two properties, a collection of providers, and the name of the default provider. This results in configuration code that could look like this: 8ouopai*sa^: 8ei]caOanre_a`ab]qhpLnkre`an9: 8lnkre`ano: 8_ha]n+: 8]``***+: 8+lnkre`ano: 8+ei]caoanre_a: 8+ouopai*sa^: The ?kjbecqn]pekjLnklanpu attribute declares the elements in the web.config file. Although it follows the pattern of all provider-based definitions in the configuration, you’re free to choose difFERENTDEFINITIONS(OWEVER THEBESTPRACTICEISTOFOLLOWTHISPATTERN This is all you need to know in order to write your own provider configuration sections.
Using the Service This serviceCANBEUSEDBYANYCODEINTHEAPPLICATION)N#HAPTER YOUSAWHOWTOUSEHANDLERS to manage images. Let’s create a handler which uses our ImageService. Start with a simple *]olt page that uses a handler, as shown in Listing 4-7. Listing 4-7. A Simple Page That Calls the Handler 8!HE?)++S/?++@P@TDPIH-*,Pn]joepekj]h++AJ± dppl6++sss*s/*knc+PN+tdpih-+@P@+tdpih-)pn]joepekj]h*`p`: 8dpihtihjo9dppl6++sss*s/*knc+-555+tdpih: 8da]`nqj]p9oanran: 8pepha:8+pepha: 8+da]`: 8^k`u: 8bknie`9bkni-nqj]p9oanran: 8`er: 8]ol6Ei]canqj]p9oanranE@9Ei]ca.Ei]caQnh9z+Ei]caD]j`han*]odt;e`90+: 8^n+: 8]ol6Ei]canqj]p9oanranE@9Ei]ca-Ei]caQnh9z+Ei]caD]j`han*]odt;e`95+: 8+`er: 8+bkni: 8+^k`u: 8+dpih: The ImageHandler.ashxFILEISAGENERICHANDLERTHATDOESNTNEEDTOBEASSIGNEDIN))34HE parameter e`CORRESPONDSTOTHE)DCOLUMNINTHEIMAGERESOURCETABLE4HISISASIMPLIFIEDSCENARIO)NREAL LIFEAPPLICATIONS ITWOULDBEBETTERTOUSEREADABLESTRINGSTODEFINETHERESOURCES (OWEVER FORLEARNINGPURPOSES THESECODESNIPPETSAREASSHORTANDEASYASPOSSIBLE The handler calls the service’s static method to retrieve the image and adds the Ei]ca object to the output stream (see Listing 4-8).
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
Listing 4-8. The Image Handler That Uses the Configurable Service 8!kkha]jNaikpaKjhu w cap w napqnj$>kkha]j%pdeoWnaikpaKjhuY7 y oap w pdeoWnaikpaKjhuY9r]hqa7 y y W?kjbecqn]pekjLnklanpu$bkjp%Y lq^he_BkjpAhaiajpBkjp w cap w napqnj$BkjpAhaiajp%pdeoWbkjpY7 y oap wpdeoWbkjpY9r]hqa7y y W?kjbecqn]pekjLnklanpu$_khkn%Y lq^he_?khknAhaiajp?khkn w cap w napqnj$?khknAhaiajp%pdeoW_khknY7 y oap wpdeoW_khknY9r]hqa7y y y y This class defines three allowed properties. The property NaikpaKjhu is of type Boolean and doesn’t need any additional definition, beyond declaring it as a ?kjbecqn]pekjLnklanpu. )FYOUHAVEAMORECOMPLEXCLASSANDWISHTODISTINGUISHBETWEENPRIVATEPROPERTIESANDTHOSE exposed to the configuration manager, this requires an additional attribute. The two named properties set in the attribute’s constructor (@ab]qhpR]hqa and EoNamqena`) are explained in Table 4-5. For the other elements (Bkjp and ?khkn), you’ll need to create your own structures. This means that an element of type 8bkjp: should contain specific attributes, just as for 8_khkn:. The definition of the BkjpAhaiajp class demonstrates this, as shown in Listing 4-11. Listing 4-11. The FontElement Class Defines a Single Element That Represents a Font lq^he__h]ooBkjpAhaiajp6?kjbecqn]pekjAhaiajp w W?kjbecqn]pekjLnklanpu$j]ia(@ab]qhpR]hqa9=ne]h(EoNamqena`9pnqa%Y WOpnejcR]he`]pkn$Ejr]he`?d]n]_pano9z]_gcnkqj` w cap w napqnj$Opnejc%pdeoW^]_gcnkqj`Y7 y oap w pdeoW^]_gcnkqj`Y9r]hqa7 y y
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
W?kjbecqn]pekjLnklanpu$bknacnkqj`(@ab]qhpR]hqa9,,,,,,(EoNamqena`9pnqa%Y WNacatOpnejcR]he`]pkn$L]ppanj9W,)5=)B])bYw2yY lq^he_OpnejcBknacnkqj` w cap w napqnj$Opnejc%pdeoWbknacnkqj`Y7 y oap w pdeoWbknacnkqj`Y9r]hqa7 y y y The OpnejcR]he`]pkn is again used to constrain the >]_gcnkqj` property. The NacatOpnejcR]he`]pkn has the same effect by using a regular expression. As you can see, the values have the same constraint. (OWEVER THEREGULAREXPRESSIONISSHORTER CLEARER ANDEASIERTOREAD5SINGREGULAREXPRESSIONSIS a good style and allows better control when searching, restricting, or replacing strings. To understand what all this creates, see the following valid web.config section. First, the custom configuration itself has to be registered (see Listing 4-13). Listing 4-13. The Configuration Definition Must Be Registered Using This Code 8_kjbecOa_pekjo: 8oa_pekjCnkqlj]ia9l]ca=lla]n]j_aCnkql: 8oa_pekj j]ia9l]ca=lla]n]j_a pula9=lnaoo*Atpajoe^ehepu*?kjbecqn]pekj*L]ca=lla]n]j_aOa_pekj ]hhksHk_]pekj9pnqa ]hhks@abejepekj9Aranusdana+: 8+oa_pekjCnkql: *** 8+_kjbecOa_pekjo: Second, the configuration section can be used (see Listing 4-14). Listing 4-14. The Configuration Definition Allows the Following Usage Scenario 8l]ca=lla]n]j_aCnkql: 8l]ca=lla]n]j_anaikpaKjhu9pnqa: 8bkjpj]ia9PeiaoJasNki]joeva9-4+: 8_khkn^]_gcnkqj`9,,,,,,bknacnkqj`9BBBBBB+: 8+l]ca=lla]n]j_a: 8+l]ca=lla]n]j_aCnkql: (OWEVER THISDOESNOTMAKESENSEIFTHEVALUESARENOTUSED$EFININGVALUESISONEISSUE using them in custom code is another. Recall the point of this chapter: providers and their configuration should assist other developers to easily replace parts of your application, simply by defining another provider. On the user interface SIDE !30.%4USESPOWERFULSERVERCONTROLSASMUCHASPOSSIBLETOCONSTRUCTTHE(4-,MARKUP/N the code side, anything that’s configurable in any way should be placed in the web.config file, another configuration file, or a database. Custom configuration is a part of the extensibility model.
179
180
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
Usage of a Custom Configuration Section Assuming the configuration section is defined, present, and filled with data, it’s time to access its values at runtime from your own code. Listing 4-15 shows how to achieve this. Listing 4-15. Access the Configuration Data 8!HE?)++S/?++@P@TDPIH-*,Pn]joepekj]h++AJ± dppl6++sss*s/*knc+PN+tdpih-+@P@+tdpih-)pn]joepekj]h*`p`: 8o_nelpnqj]p9oanran: lnkpa_pa`rke`L]ca[Hk]`$k^fa_poaj`an(Arajp=ncoa% w r]n_kjbec9± $=lnaoo*Atpajoe^ehepu*?kjbecqn]pekj*L]ca=lla]n]j_aOa_pekj%± Ouopai*?kjbecqn]pekj*?kjbecqn]pekjI]j]can*CapOa_pekj$± l]ca=lla]n]j_aCnkql+l]ca=lla]n]j_a%7 Opnejc>qeh`ano^9jasOpnejc>qeh`an$%7 o^*=llaj`$8d.:OappejcoejpdaL]ca=lla]n]j_aOa_pekj68+d.:%7 o^*=llaj`$Opnejc*Bkni]p$NaikpaKjhu6w,y8^n:(_kjbec*NaikpaKjhu%%7 o^*=llaj`$Opnejc*Bkni]p$Bkjpj]ia]j`oeva6w,yw-y8^n:(± _kjbec*Bkjp*J]ia(_kjbec*Bkjp*Oeva%%7 o^*=llaj`$± Opnejc*Bkni]p$>]_gcnkqj`]j`bknacnkqj`_khkn6w,yw-y8^n:(± _kjbec*?khkn*>]_gcnkqj`(_kjbec*?khkn*Bknacnkqj`%%7 h^h?kjbec*Patp9o^*PkOpnejc$%7 y 8+o_nelp: 8dpih: 8da]`e`9Da]`-nqj]p9oanran: 8pepha:?qopki?kjbecqn]pekjOa_pekjAt]ilha8+pepha: 8+da]`: 8^k`u: 8bknie`9bkni-nqj]p9oanran: 8`er: 8]ol6H]^ahnqj]p9oanranE@9h^h?kjbec:8+]ol6H]^ah: 8+`er: 8+bkni: 8+^k`u: 8+dpih: The ?kjbecqn]pekjI]j]can class is the entry point into the configuration. You can address any section here by giving the full path or, at least, a distinct part of the leaf path. As shown in the section about providers, “The Provider Model,” there are several ways to handle these strings. Consider attributes, constant literals, and helper classes. Once you have the section, and the section is a private type, you can cast as shown in the listing. The properties will represent the current values in the configuration. This will not only handle private data but also give fully typed access to the elements. 4HECONFIGURATIONISBASEDONTHESERIALIZATIONANDDESERIALIZATIONFEATURESTHATTHEBASECLASS provides. This could make it difficult to read or to modify the values in the web.configFILE)FYOU UNDERSTANDHOWTHESERIALIZERWORKSANDCANCREATESIMILARSTRINGS by hand, your configuration class should function correctly as is.
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
Accessing the Configuration Declaratively You now have an extended configuration model allowing the storage of complex values in a clearly DEFINEDWAY4HEPROGRAMMATICACCESSISEASY(OWEVER !30.%4FAVORSDECLARATIVETECHNIQUES Making the configuration accessible from markup would be ideal.
Extending the Expression Binding Syntax )NTEGRATEDEXPRESSIONS play an important role in accessing data dynamically. You probably work with data binding expressions following the 8!!: pattern. A different declarative syntax is available, with this pattern: 8! !: This is also an extensible model, allowing the creation of your own syntax in order to access data within the markup: 8]ol6hepan]hpatp98! Iu?kjbec6Cap@]p]!:nqj]p9oanran+: Read this as an enhancement to the configuration extension described in the section “ExtendINGTHE#ONFIGURATIONv)TTRANSFERSACCESSTOTHEMARKUP$EVELOPERSCANACCESSDATAWITHOUT writing explicit code. Now page designers working with .aspx pages can add specific formatting options without doing any coding tasks.
Introduction to Expression Syntax Expression builders process expressions. These build code from expressions during the pageprocessing phase—the parsing procedure. Expressions have this distinct pattern: 8! WlnabetY6W`a_h]n]pekjY!: The first part, the prefix, maps to a type that handles the expression. Any string will do for the prefix. The colon is mandatory, but you can enter whatever you like in the declaration part. Anything from the colon to the end of the expression is treated as a Opnejc and processed at once. One common usage is the accessing of the =llOappejco section in web.config)TFOLLOWSTHISPATTERN 8! =llOappejco6GauJ]ia!: 4HEPREFIXISh!PP3ETTINGSv)TDETERMINESTHEEXPRESSIONBUILDERVIAAMAPPINGEXPLAINED in the “Declare the Prefix” section) to a built-in class called =llOappejcoAtlnaooekj>qeh`an. The expression builder must be able to handle the remaining part, “KeyName” in the example. &ORLOCALIZEDRESOURCESTHEREISANOTHERBUILT INCLASS 8! Naokqn_ao6Naokqn_a?]packnu(J]ia!: The prefix is now “Resources”. The expression builder used behind the scenes is the Naokqn_aAtlnaooekj>qeh`an. There is also another, ?kjja_pekjOpnejcoAtlnaooekj>qeh`an, which makes connection strings available in markup. All expression builders derive from the abstract base class Atlnaooekj>qeh`an. The extensibility concept follows the common pattern. By implementing this base class, you can build your own expression builder.
How It Works Internally )NTERNALLY THE expression builder creates a code snippet. This code is inserted into the page during the parsing step. The compiler treats this code as part of your custom code and creates the page object. This requires the code snippet to follow specific rules. Essentially, the code is an assignment.
181
182
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
)TASSIGNSAVALUETOAPROPERTYOFACONTROL7HERETHEVALUECOMESFROMMIGHTREQUIRECOMPLEX code, but the assignment is simple and limits the usage of such expressions. The most evident limitation is that usage without a control is not allowed. The following code will not function: 8`er: 8! Iu?kjbec6R]hqa-!: 8+`er: )NSTEAD YOUMUSTUSEACONTROL 8`er: 8]ol6h]^ahnqj]p9oanranPatp98! Iu?kjbec6R]hqa-!:+: 8+`er: (OWEVER USINGTHEPREFIXISNOTENOUGHTOPUTITINTOOPERATION&IRST YOUMUSTMAPTHEPREFIX to a specific type.
Declare the Prefix The declaration of the prefix takes place in the web.config file. The path to the configuration element is 8ouopai*sa^:8_kileh]pekj:8atlnaooekj>qeh`ano:. The 8atlnaooekj>qeh`ano: element is usually absent. Add the following element to define a new mapping between a prefix and a type: 8atlnaooekj>qeh`ano: 8]``atlnaooekjLnabet9lnabetpula9pula(]ooai^hu+: 8+atlnaooekj>qeh`ano: The pula follows the common schema of an assembly reference:
s .AMESPACE#LASS !SSEMBLY 6ERSION #ULTURE 0UBLIC+EY4OKEN
s .AMESPACE#LASS !SSEMBLY
s .AMESPACE#LASS ??CODE
The first form defines the fully qualified name of an assembly. The last references the App_Code FOLDER IFTHEAPPROPRIATEPROJECTTYPEISUSEDIN6ISUAL3TUDIO.OTETHETWOUNDERSCORESBEFORETHE word “code” here.) An example could look like this:
s !PRESS%XTENSIONS-Y"UILDER !PRESS%XTENSIONS Now you need to create the type. As mentioned, the abstract base class is where you start.
How the Expression Builder Works The expression builder has only a few methods. Using these methods, it can parse the expression and generate the required code. Parsing is initiated using the L]noaAtlnaooekj method)FTHISSTEP is successful, the Cap?k`aAtlnaooekj method generates a code snippet. The only condition is that the compiler must be able to assign the snippet to a property. Therefore, the snippet must form the right-hand side of an assignment: _kjpnkh*Lnklanpu98pdeoeopda_k`aatlnaooekj:7 While the right-hand side is merely a method call, this method can contain any amount of code (meaning that even the most complex operations are possible). )FTHEPARSINGFAILS YOUSHOULDTHROWANEXCEPTION/THERWISE THECap?k`aAtlnaooekj method is invoked to create the code using the Code Document Object Model (CodeDOM). Although a short explanation follows, this book does not teach CodeDOM in depth. Refer to the MSDN documentation to learn more about source code generation: dppl6++io`j*ie_nkokbp*_ki+aj)qo+he^n]nu+ ouopai*_k`a`ki*]opx.
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
4ABLE LISTSTHEMETHODSYOUHAVETOIMPLEMENTTOADDANEXPRESSIONBUILDERTOANOPERATION Table 4-6. Methods of an Expression Builder
Method
Description
Ar]hq]paAtlnaooekj
Returns the value for non-compiled pages (see the section “Accessing Settings for Non-Compiled Pages” in this chapter for details)
Cap?k`aAtlnaooekj
Creates the code snippet to insert into the page
L]noaAtlnaooekj
Parses the syntax of the expression
OqllknpoAr]hq]pa
)NDICATESWHETHERTHISEXPRESSIONBUILDERSUPPORTSNON COMPILEDPAGES
These methods are sufficient for creating our own expression builder.
Creating an Expression Builder )NTHEFIRST example, you need direct access to values stored in a SQL Server database. You are using the same database and table you created earlier in the section “Creating a Custom ProviderBased Service.” Please also refer to Figure 4-2. Specific criteria are used to retrieve the right values. The prefix is called “Cfg” and the syntax of the expressions looks like this: 8! ?bc6GauBNKI_]packnuSDANApula!: The syntax is similar to SQL, which makes it more readable. The type refers to the type of data, SUCHAShIMAGEvORhLABELv)NTHEMARKUPPORTION THISWOULDLOOKLIKETHEFOLLOWING 8]ol6H]^ahE@9h^hDa]`annqj]p9oanran± Patp98! ?bc6Da]`anBNKIL]ca@]p]SDANAh]^ah!::8+]ol6H]^ah: The following code is the same for an image: 8]ol6Ei]caE@9eicDa]`annqj]p9oanran± Ei]caQnh98! ?bc6Da]`anBNKIL]ca@]p]SDANAei]ca!::8+]ol6Ei]ca: 4HECREATEDCODEFORTHEIMAGEEXAMPLELOOKSLIKEhIMAGESHEADERPNGv/NCEDEFINED 6ISUAL 3TUDIORECOGNIZESTHEEXPRESSIONSYNTAX ASSHOWNIN&IGURE
Figure 4-6. The private expression builder appears in Visual Studio’s expression dialog. /NESOLUTIONISSHOWNIN,ISTING
183
184
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
Listing 4-16. Using Expressions for Retrieving Configuration Data from a Database qoejcOuopai7 qoejcOuopai*Sa^*?kileh]pekj7 qoejcOuopai*?k`a@ki7 j]iaol]_a=lnaoo*Atpajoe^ehepu*Atlnaooekjo w lq^he__h]oo?bcAtlnaooekj6Atlnaooekj>qeh`an w lner]paop]pe_?kjbec@]p]@]p]?kjpatp_pt7 lner]paop]pe_rke`Ajoqna?bc?kjpatp$% w _pt9jas?kjbec@]p]@]p]?kjpatp$%7 y lq^he_op]pe_opnejcCap?bc$opnejcgau(opnejc_]packnu(opnejcpula% w Ajoqna?bc?kjpatp$%7 r]nnao9bnkinksej_pt*]oljap[?kjbecqn]pekjo± sdananks*_bc[gau99gau± ""nks*_bc[_]packnu99_]packnu± ""nks*_bc[pula99pula± oaha_pnks*_bc[_kjpajp7 napqnjnao*BenopKn@ab]qhp8opnejc:$%7 y lq^he_kranne`a?k`aAtlnaooekjCap?k`aAtlnaooekj$± Ouopai*Sa^*QE*>kqj`LnklanpuAjpnuajpnu(± k^fa_pl]noa`@]p](± Atlnaooekj>qeh`an?kjpatp_kjpatp% w AtlnaooekjR]hqao_bcR]hqao9l]noa`@]p]]oAtlnaooekjR]hqao7 eb$_bcR]hqao99jqhh%pdnksjas=ncqiajpAt_alpekj$l]noa`@]p]%7 ?k`aLneieperaAtlnaooekjWY_=nc9jas?k`aLneieperaAtlnaooekjWY w jas?k`aLneieperaAtlnaooekj$_bcR]hqao*Gau%( jas?k`aLneieperaAtlnaooekj$_bcR]hqao*?]packnu%( jas?k`aLneieperaAtlnaooekj$_bcR]hqao*Pula% y7 ?k`aPulaNabanaj_aAtlnaooekjp9jas?k`aPulaNabanaj_aAtlnaooekj$± pulakb$?bcAtlnaooekj%%7 ?k`aIapdk`EjrkgaAtlnaooekjatl9jas?k`aIapdk`EjrkgaAtlnaooekj$p(± Cap?bc(_=nc%7 napqnjatl7 y lq^he_kranne`ak^fa_pL]noaAtlnaooekj$opnejcatlnaooekj(± PulalnklanpuPula(± Atlnaooekj>qeh`an?kjpatp_kjpatp% w napqnjPkgajL]noan*L]noa$atlnaooekj%7 y
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
lq^he_kranne`a^kkhOqllknpoAr]hq]pa w cap w napqnjb]hoa7 y y lq^he_kranne`ak^fa_pAr]hq]paAtlnaooekj$k^fa_pp]ncap(± Ouopai*Sa^*QE*>kqj`LnklanpuAjpnuajpnu( k^fa_pl]noa`@]p](± Atlnaooekj>qeh`an?kjpatp_kjpatp% w napqnj^]oa*Ar]hq]paAtlnaooekj$p]ncap(ajpnu(l]noa`@]p](_kjpatp%7 y nacekj?kjbecR]hqao _h]ooAtlnaooekjR]hqao w lq^he_opnejcGauwcap7oap7y lq^he_opnejc?]packnuwcap7oap7y lq^he_opnejcPulawcap7oap7y y aj`nacekj nacekjOeilhaPkgajL]noan op]pe__h]ooPkgajL]noan w op]pe_AtlnaooekjR]hqaor]hqao7 op]pe_PkgajL]noan$% w r]hqao9jasAtlnaooekjR]hqao$%7 y ejpanj]hop]pe_AtlnaooekjR]hqaoL]noa$opnejcpkL]noa% w eb$Opnejc*EoJqhhKnAilpu$pkL]noa%% pdnksjas=ncqiajpJqhhAt_alpekj$pkL]noa%7 ejpe9,7 opnejc_qnnajpPkgaj9Opnejc*Ailpu7 ++r]hqaBNKI_]pSDANApula sdeha$pnqa% w _d]n_9pkL]noaWeY7 osep_d$_% w _]oa#B#6 eb$pkL]noa*Oq^opnejc$e(1%*Amq]ho$BNKI%% w r]hqao*Gau9_qnnajpPkgaj*Pnei$%7 _qnnajpPkgaj9Opnejc*Ailpu7 e'907
185
186
C H APT ER 4 N PROVIDER S A ND C ONFIG U R A TION
y ^na]g7 _]oa#S#6 eb$pkL]noa*Oq^opnejc$e(2%*Amq]ho$SDANA%% w r]hqao*?]packnu9_qnnajpPkgaj*Pnei$%7 _qnnajpPkgaj9Opnejc*Ailpu7 e'917 y ^na]g7 `ab]qhp6 _qnnajpPkgaj'9_7 ^na]g7 y eb$''e8pkL]noa*Hajcpd%_kjpejqa7 r]hqao*Pula9_qnnajpPkgaj7 ^na]g7++aj`sdeha y napqnjr]hqao7 y y aj`nacekj y y 4HECREATIONOFDYNAMICCODEISTHEMOSTIMPORTANTPART(OWEVER ITDOESNTMAKESENSE to do everything dynamically. This is why the code for retrieving data is moved to the static method, Cap?bc. The dynamic portion calls that method, which leads to generated code, such as the following: h^hDa]`an*Patp9Cap?bc$***%7 4HEMETHODISSTATICBECAUSETHEPRIVATEEXPRESSIONBUILDERISNOTINSTANTIATEDATRUNTIME)TS ADESIGN TIMETOOLWHICHSUPPORTSTHEPAGEPARSERANDDESIGN TIMEEXPERIENCEIN6ISUAL3TUDIO Only the assignment remains in the page’s code after compilation. The procedure commences with the L]noaAtlnaooekj method. The object created here appears later as the l]noa`@]p] parameter of the Cap?k`aAtlnaooekj method. The PkgajL]noan class in the EXAMPLEDEMONSTRATESHOWTOPARSECUSTOMSTRINGS)TANALYSESTHESTRINGAFTERTHEEXPRESSIONSCOLON and creates three required parameters from it. The private class, AtlnaooekjR]hqao, stores the values, while the Cap?k`aAtlnaooekj methodBUILDSTHENECESSARYCODE)TMUSTRETURNANOBJECTOFTYPEOuopai* ?k`a@ki*?k`aAtlnaooekj, which will contain a code fragment assignable to the right-hand side of an ASSIGNMENTSTATEMENT2EFERTOTHENEXTTABLETOVIEWTHEMOSTIMPORTANT#ODE$/-FUNCTIONS)NTHIS EXAMPLE )USEASIMPLECALLTOASTATICMETHODSOTHATTHECODEGENERATIONISASSIMPLEASPOSSIBLE4HE core element is the method invocation: jas?k`aIapdk`EjrkgaAtlnaooekj$p(Cap?bc(_=nc% The argument cArg is the collection of private parameters (see Table 4-7).
C H A P T E R 4 N P R O V I D E R S A N D C O N F I G U R A T I O N
Table 4-7. Important CodeDOM Methods
Method
Description
?k`aAtlnaooekj
An abstract base class for all CodeDOM types
?k`aPulaNabanaj_aAtlnaooekj
A type reference, such as a type used in code
?k`aLneieperaAtlnaooekj
Any code expression, which does not fit into other categories
?k`aIapdk`EjrkgaAtlnaooekj
A method call which requires, as parameters, the type where the method is defined, the name of the method, and an array of code fragments used as parameters for the method
The Cap?bc method follows the same pattern used in the configuration example earlier in the section “Extending the Expression Binding Syntax.” This draws from the @]p]?kjpatp class and ,).1TO31,TORETRIEVETHEVALUES4HENAMEOFTHECONTEXT ?kjbec@]p]@]p]?kjpatp, is based on the definition in ConfigData.dbml.
Accessing Settings for Non-Compiled Pages )FPAGESCONTAIN mainly static content, the compilation step can be suppressed. Code expressions and expression builders will still function. Moreover, expression builders which support non-compiled pages offer a different way of handling the code creation. You can suppress the compilation by setting the following attribute in the K@U6DPIH7 dkp@kc9eoEA;arajp*on_Ahaiajp6a*p]ncap7 sdeha$dkp@kc*e`9pepha>]n""dkp@kc*p]cJ]ia9pkl@kc%w dkp@kc9eoEA;dkp@kc*l]najpAhaiajp6dkp@kc*l]najpJk`a7 eb$dkp@kc99jqhhxxdkp@kc*e`99jqhhxxdkp@kc*p]cJ]ia99jqhh% napqnj7 y eb$dkp@kc*e`99pepha>]n%w kbboapt9eoEA;arajp*_heajpT6a*_heajpT7 kbboapu9eoEA;arajp*_heajpU6a*_heajpU7 jksT9l]noaEjp$sde_d@kc*opuha*habp%7 jksU9l]noaEjp$sde_d@kc*opuha*pkl%7 ``Aj]^ha`9pnqa7 `k_qiajp*kjikqoaikra9``7 y y bqj_pekj``$a%w eb$``Aj]^ha`%napqnj7 sde_d@kc*opuha*habp9eoEA;± jksT'arajp*_heajpT)kbboapt6jksT'a*_heajpT)kbboapt7 sde_d@kc*opuha*pkl9eoEA;± jksU'arajp*_heajpU)kbboapu6jksU'a*_heajpU)kbboapu7 napqnjb]hoa7 y bqj_pekjde`aIa$%w sde_d@kc*opuha*reoe^ehepu9de``aj7 y bqj_pekjodksIa$%w sde_d@kc*opuha*reoe^ehepu9reoe^ha7 y `k_qiajp*kjikqoa`ksj9``Ejep7 `k_qiajp*kjikqoaql9Bqj_pekj$``Aj]^ha`9b]hoa%7 8+o_nelp: This code portion is shown for completeness. It is used to move the editor window around. When editing the page, it’s helpful to see the page, which might be partly hidden by the overlaid WINDOWSEE,ISTING
C H A P T E R 5 N E X T E N D I N G T H E R E S O U R C E M O D E L
Listing 5-21. Part 2: The JavaScript Editor Defined in the edit.ascx Control: the Resource Access 8o_nelph]jcq]ca9f]r]o_nelppula9patp+f]r]o_nelp: r]nl]najpK^f7 bqj_pekjodksIaoo]ca$ioc%w r]nh]^ah9`k_qiajp*capAhaiajp>uE`$#Iaoo]caH]^ah#% h]^ah*ejjanPatp9ioc7 h]^ah*opuha*`eolh]u9$ioc*hajcpd99,%;#jkja#6#^hk_g#7 h]^ah*opuha*_khkn9,,BB//7 y bqj_pekjodksAnnkn$ioc%w r]nh]^ah9`k_qiajp*capAhaiajp>uE`$#Iaoo]caH]^ah#% h]^ah*ejjanPatp9ioc7 h]^ah*opuha*`eolh]u9$ioc*hajcpd99,%;#jkja#6#^hk_g#7 h]^ah*opuha*_khkn9BB,,,,7 y ++Pdaoabqj_pekjo_]hhpdaSa^Oanre_aiapdk`* bqj_pekjejepH]jcq]cao$%w odksIaoo]ca$?]hhejcOanran***%7 NaotA`epkn*Naokqn_aOanre_a*Cap=hhH]jcq]cao$ejepH]jcq]cao?]hh^]_g(± ejepH]jcq]caoAnnkn%7 y bqj_pekjejepH]jcq]cao?]hh^]_g$naoqhp%w r]noahHk]`9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 r]noah?klu9`k_qiajp*capAhaiajp>uE`$#Naot?kluOaha_pkn#%7 oahHk]`*klpekjo*hajcpd9,7 oah?klu*klpekjo*hajcpd9,7 bkn$e9,7e8naoqhp*haj7e''%w oahHk]`*klpekjoWeY9jasKlpekj$naoqhp*_qhpqnaJ]iaWeY(± naoqhp*_qhpqnaE@WeY%7 oah?klu*klpekjoWeY9jasKlpekj$naoqhp*_qhpqnaJ]iaWeY(± naoqhp*_qhpqnaE@WeY%7 y odksIaoo]ca$%7 `k_qiajp*capAhaiajp>uE`$#pda?kjpajp?kjp]ejan#%*oap=ppne^qpa$`eo]^ha`(± b]hoa%7 y bqj_pekjejepH]jcq]caoAnnkn$annkn%w odksAnnkn$annkn*cap[iaoo]ca$%%7 y bqj_pekjo]raIa$%w odksIaoo]ca$O]rapda`]p]***%7 r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]np^9`k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*ejjanPatp7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7
233
234
C H APT ER 5 N EXT ENDING THE R ES OU R C E MODEL
++Patp NaotA`epkn*Naokqn_aOanre_a*O]raNaokqn_a$l]caE`(_pnhE`'*Patp(± p^(_qhpqna(pdaia(± o]raIa?]hh^]_gPatp(± o]raIaAnnkn%7 y ++_hkoa]bpano]raeo`kja eb$`k_qiajp*capAhaiajp>uE`$#o]raIa?hkoa?da_g^kt#%*_da_ga`99pnqa%w de`aA`epkn$%7 y y ++Pdeoeopda_]hh^]_gbqj_pekjpd]plnk_aooaopdaSa^Oanre_anapqnjr]hqa bqj_pekjo]raIa?]hh^]_gPatp$naoqhp(arajp=nco%w eb$l]najpK^f*cap=ppne^qpa$]ppne^qpao%*oa]n_d$+PkkhPel+%9)-%w odksIaoo]ca$O]rapda`]p]****%7 ++Pkkhpel r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 r]np^9± `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*O]raNaokqn_a$l]caE`(± _pnhE`'*PkkhPel(± p^(_qhpqna(pdaia(± o]raIa?]hh^]_gPkkhpel(± o]raIaAnnkn%7 y yahoaw o]raIa?]hh^]_gPkkhpel$jqhh(jqhh%7 y y bqj_pekjo]raIa?]hh^]_gPkkhpel$naoqhp(arajp=nco%w eb$l]najpK^f*cap=ppne^qpa$]ppne^qpao%*oa]n_d$+AnnknIaoo]ca+%9)-%w odksIaoo]ca$O]rapda`]p]*****%7 ++Pkkhpel r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 r]np^9± `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktAnnknIaoo]ca*?heajpE@!:#%*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*O]raNaokqn_a$l]caE`(± _pnhE`'*AnnknIaoo]ca(± p^(_qhpqna(pdaia(± o]raIa?]hh^]_gReoe^ehepu(± o]raIaAnnkn%7 y yahoaw o]raIa?]hh^]_gReoe^ehepu$jqhh(jqhh%7 y
C H A P T E R 5 N E X T E N D I N G T H E R E S O U R C E M O D E L
y bqj_pekjo]raIa?]hh^]_gReoe^ehepu$naoqhp(arajp=nco%w ++Lnklanpeao66Reoe^ha r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 r]n_^9`k_qiajp*capAhaiajp>uE`$#8!9?dg>ktReoe^ha*?heajpE@!:#%7 eb$_^*_da_ga`99b]hoa%w NaotA`epkn*Naokqn_aOanre_a*O]raNaokqn_a$l]caE`(± _pnhE`'*Reoe^ha(± B]hoa(_qhpqna(pdaia(± o]raIa?]hh^]_gAnnknIaoo]ca(± o]raIaAnnkn%7 yahoaw NaotA`epkn*Naokqn_aOanre_a*O]raNaokqn_a$l]caE`(± _pnhE`'*Reoe^ha(± Pnqa(_qhpqna(pdaia(± o]raIa?]hh^]_gAnnknIaoo]ca(± o]raIaAnnkn%7 y y y bqj_pekjo]raIa?]hh^]_gAnnknIaoo]ca$naoqhp(arajp=nco%w odksIaoo]ca$%7 y bqj_pekjo]raIaAnnkn$ann%w odksAnnkn$ann*cap[iaoo]ca$%%7 y bqj_pekjhk]`Ia$%w odksIaoo]ca$Hk]`Naokqn_aobnkiOanran***%7 r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 `k_qiajp*capAhaiajp>uE`$#H]^ah?kjpnkhE`#%*ejjanPatp9_pnhE`7 `k_qiajp*capAhaiajp>uE`$#H]^ahL]caE`#%*ejjanPatp9l]caE`7 ++Patp NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*Patp(± _qhpqna(pdaia(± hk]`Ia?]hh^]_gPatp(± hk]`IaAnnkn%7 y y bqj_pekjhk]`Ia?]hh^]_gPatp$naoqhp(arajp=nco%w `k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*ejjanPatp9naoqhp7 eb$l]najpK^f*cap=ppne^qpa$]ppne^qpao%*oa]n_d$+PkkhPel+%9)-%w
235
236
C H APT ER 5 N EXT ENDING THE R ES OU R C E MODEL
odksIaoo]ca$Hk]`Naokqn_aobnkiOanran****%7 ++Pkkhpel r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*Pkkhpel(± _qhpqna(pdaia(± hk]`Ia?]hh^]_gPkkhpel(± hk]`IaAnnkn%7 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± `eo]^ha`9b]hoa7 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± opuha*^]_gcnkqj`?khkn9sdepa7 y yahoaw `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± `eo]^ha`9pnqa7 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± opuha*^]_gcnkqj`?khkn9oehran7 capAnnknIaoo]ca$%7 y y
bqj_pekjhk]`Ia?]hh^]_gPkkhpel$naoqhp(arajp=nco%w `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± ejjanPatp9naoqhp7 capAnnknIaoo]ca$%7 y bqj_pekjcapAnnknIaoo]ca$%w eb$l]najpK^f*cap=ppne^qpa$]ppne^qpao%*oa]n_d$+AnnknIaoo]ca+%9)-%w odksIaoo]ca$Hk]`Naokqn_aobnkiOanran*****%7 ++AnnknIaoo]ca r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]npdaia9#8!9Pdaia!:#7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*AnnknIaoo]ca(± _qhpqna(pdaia(± hk]`Ia?]hh^]_gAnnknIaoo]ca(± hk]`IaAnnkn%7 y yahoaw ++Lnk_aa`sepdjatp capReoe^ehepu$%7 y y bqj_pekjhk]`Ia?]hh^]_gAnnknIaoo]ca$naoqhp(arajp=nco%w
C H A P T E R 5 N E X T E N D I N G T H E R E S O U R C E M O D E L
++O]ranaoqhp `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktAnnknIaoo]ca*?heajpE@!:#%*± ejjanPatp9naoqhp7 ++Lnk_aa`sepdjatp capReoe^ehepu$%7 y bqj_pekjcapReoe^ehepu$%w r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 r]npdaia9#8!9Pdaia!:#7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*Reoe^ha(± _qhpqna(pdaia(± hk]`Ia?]hh^]_gReoe^ha(± hk]`IaAnnkn%7 y y bqj_pekjhk]`Ia?]hh^]_gReoe^ha$naoqhp(arajp=nco%w ++O]ranaoqhp eb$naoqhp99jqhhxxnaoqhp99pnqaxxnaoqhp99Pnqa%w `k_qiajp*capAhaiajp>uE`$#8!9?dg>ktReoe^ha*?heajpE@!:#%*_da_ga`9± pnqa7 yahoaw `k_qiajp*capAhaiajp>uE`$#8!9?dg>ktReoe^ha*?heajpE@!:#%*_da_ga`9± b]hoa7 y ++@kja(_ha]niaoo]ca odksIaoo]ca$%7 y
bqj_pekjhk]`IaAnnkn$ann%w odksAnnkn$ann*cap[iaoo]ca$%%7 y bqj_pekj_kluIa$%w odksIaoo]ca$Hk]`Naokqn_aobnkiOanran***%7 r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]noah9`k_qiajp*capAhaiajp>uE`$#Naot?kluOaha_pkn#%7 r]npdaia9#8!9Pdaia!:#7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 ++Patp NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*Patp(± _qhpqna(pdaia(± _kluIa?]hh^]_gPatp(± hk]`IaAnnkn%7 y y
237
238
C H APT ER 5 N EXT ENDING THE R ES OU R C E MODEL
bqj_pekj_kluIa?]hh^]_gPatp$naoqhp(arajp=nco%w `k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*ejjanPatp9naoqhp7 odksIaoo]ca$Hk]`Naokqn_aobnkiOanran****%7 ++Pkkhpel r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]noah9`k_qiajp*capAhaiajp>uE`$#Naot?kluOaha_pkn#%7 r]npdaia9#8!9Pdaia!:#7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*Pkkhpel(± _qhpqna(pdaia(± _kluIa?]hh^]_gPkkhpel(± hk]`IaAnnkn%7 y y bqj_pekj_kluIa?]hh^]_gPkkhpel$naoqhp(arajp=nco%w odksIaoo]ca$Hk]`Naokqn_aobnkiOanran*****%7 ++Pkkhpel r]nl]caE`9l]najpK^f*cap=ppne^qpa$l]caE@%7 r]n_pnhE`9l]najpK^f*cap=ppne^qpa$_pnhE@%7 r]noah9`k_qiajp*capAhaiajp>uE`$#Naot?kluOaha_pkn#%7 r]npdaia9#8!9Pdaia!:#7 eb$oah*klpekjo*hajcpd:,%w r]n_qhpqna9oah*klpekjoWoah*oaha_pa`Ej`atY*r]hqa7 NaotA`epkn*Naokqn_aOanre_a*Hk]`Naokqn_a$l]caE`(± _pnhE`'*AnnknIaoo]ca(± _qhpqna(pdaia(± _kluIa?]hh^]_gAnnknIaoo]ca(± hk]`IaAnnkn%7 y y bqj_pekj_kluIa?]hh^]_gAnnknIaoo]ca$naoqhp(arajp=nco%w `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktAnnknIaoo]ca*?heajpE@!:#%*ejjanPatp9± naoqhp7 odksIaoo]ca$%7 y bqj_pekjodksA`epkn$]hhksDpih(l]najp%w odksIaoo]ca$%7 `k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*`eo]^ha`9pnqa7 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*`eo]^ha`9pnqa7 `k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*± opuha*^]_gcnkqj`?khkn9oehran7 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± opuha*^]_gcnkqj`?khkn9oehran7 `k_qiajp*capAhaiajp>uE`$#AnnknIaoo]ca#%*opuha*`eolh]u9jkja7 l]najpK^f9l]najp7 ++ei]casepd]ppne^qpanabanaj_ejcpkk^fa_p`]p] r]nap^9`k_qiajp*capAhaiajp>uE`$#patp>kt?kjp]ejan#%7 ++PK@K6oqllknpikna]ppne^qpao r]n]ppn9l]najpK^f*cap=ppne^qpa$]ppne^qpao%7 r]n]9]ppn*olhep$(%7 bkn$e9,7e8]*hajcpd7e''%w
C H A P T E R 5 N E X T E N D I N G T H E R E S O U R C E M O D E L
osep_d$]WeY%w _]oaPepha6 ^na]g7 _]oaPatp6 `k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*± `eo]^ha`9b]hoa7 `k_qiajp*capAhaiajp>uE`$#8!9A`epPatp>kt*?heajpE@!:#%*± opuha*^]_gcnkqj`?khkn97 ap^*opuha*reoe^ehepu9#reoe^ha#7 ap^*opuha*`eolh]u9#ejheja#7 ^na]g7 _]oaPkkhPel6 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± `eo]^ha`9b]hoa7 `k_qiajp*capAhaiajp>uE`$#8!9Patp>ktPkkhpel*?heajpE@!:#%*± opuha*^]_gcnkqj`?khkn9sdepa7 ^na]g7 _]oaAnnknIaoo]ca6 `k_qiajp*capAhaiajp>uE`$#AnnknIaoo]ca#%*opuha*`eolh]u9^hk_g7 ^na]g7 y y hk]`Ia$%7 odksIa$%7 y bqj_pekjde`aA`epkn$%w r]nap^9`k_qiajp*capAhaiajp>uE`$#patp>kt?kjp]ejan#%7 ap^*opuha*reoe^ehepu9#de``aj#7 ap^*opuha*`eolh]u9#jkja#7 de`aIa$%7 y bqj_pekjejepA`epkn$%w ++ejep r]noah9`k_qiajp*capAhaiajp>uE`$#NaotH]jcq]caOaha_pkn#%7 eb$oah*klpekjo*hajcpd99,%w ejepH]jcq]cao$%7 y de`aA`epkn$%7 y 8+o_nelp: This longer JavaScript code block invokes the Web Services. The pattern is, according to !JAXCONVENTION ASYNCHRONOUS/NEMETHODCALLSTHE7EB3ERVICE DEFININGCALLBACKMETHODSAS parameters. Once the Web Service responds, the callback method is invoked. This could be either a success or error path, depending on the outcome. The result is written back into the elements of the editor window. Other methods transfer data from the editor window to the server. Due to the complexity, several methods are required:
s 3AVE88%ACH3AVEMETHODSAVESAPARTICULARCONTROLSCONTENT
s ,OAD88%ACH,OADMETHODLOADSTHECONTENTFROMTHESERVER
s #OPY884HECOPYMETHODCOPIESFROMONELANGUAGETOANOTHER
s 88#ALLBACK4HECALLBACKCONTAININGTHEDATA
239
240
C H APT ER 5 N EXT ENDING THE R ES OU R C E MODEL
Some callback methods invoke the subsequent step. For the Load procedure, the Hk]`Ia func tion is the entry point. In the callback, the next control’s resource is retrieved from the server and read in its own callback method. This creates a chain of callbacks that are dependent on each other. This is similar for the Save procedure. After defining the access methods to the server, the editor WINDOWCANBECREATED ASSHOWNIN,ISTING Listing 5-22. Part 3: The Editor Popup Window Definition 8`ere`9pdaH]uanopuha9^]_gcnkqj`)_khkn6oehran7lkoepekj6]^okhqpa7pkl60,lt7habp6 -31lt7se`pd630,lt7^kn`an6okhe`.ltcn]u7 l]``ejc61lt7bkjp)b]iehu6Ran`]j]7_khkn6>hqa7bkjp)oeva6-,lp7reoe^ehepu6reoe^ha7 v)ej`at6-,,,,: 8p]^ha^kn`an9,se`pd9-,,!^c_khkn9pn]jol]najp_ahhol]_ejc9,_ahhl]``ejc9,: 8pn: 8p`se`pd9-,,!: 8p]^ha^kn`an9,^c_khkn9^hqase`pd9-,,!_ahhol]_ejc9,_ahhl]``ejc9/ daecdp9/.: 8pn: 8p`e`9pepha>]nopuha9_qnokn6ikrase`pd951!: 8eh]uanse`pd9-,,!kjoaha_pop]np9napqnjb]hoa: 8h]uanse`pd9-,,!kjIkqoakran9eoDkp9pnqa7eb$eoJ0%``J0$pdaH]uan% kjIkqoakqp9eoDkp9b]hoa: 8ol]jopuha9bkjp)b]iehu6Ran`]j]7bkjp)oeva6-.lp7bkjp)saecdp6^kh`7 _khkn6Sda]p:KjhejaNaokqn_aA`epkn8+ol]j: 8+h]uan: 8+eh]uan: 8+p`: 8p`opuha9_qnokn6d]j`r]hecj9pkl]hecj9necdpse`pd91!: 8`erkj_he_g9de`aA`epkn$%7napqnjb]hoaopuha9l]``ejc6.lt7_khkn6bbbbbb7 ^]_gcnkqj`)_khkn6Na`7se`pd64lt7daecdp6-.lt7bkjp)oeva65lt7 bkjp)b]iehu6Ran`]j]7patp)`a_kn]pekj6jkja7patp)]hecj6_ajpan7 ranpe_]h)]hecj6pkl: t 8+`er: 8+p`: 8+pn: 8pn: 8p`se`pd9-,,!^c_khkn9BBBBBBopuha9l]``ejc60lt_khol]j9. `eo]^ha`9pnqae`9pda?kjpajp?kjp]ejan: 8p]^ha: 8pn: 8p`: H]jcq]caoaha_pekj6 8+p`: 8p`: 8oaha_pe`9NaotH]jcq]caOaha_pknkj_d]jca9hk]`Ia$%7: 8+oaha_p: 8+p`: 8+pn: 8pn: 8p`: ?klubnkih]jcq]ca6 8+p`: 8p`: 8oaha_pe`9Naot?kluOaha_pknkj_d]jca9_kluIa$%7: 8+oaha_p: 8+p`:
C H A P T E R 5 N E X T E N D I N G T H E R E S O U R C E M O D E L
8+pn: 8+p]^ha: 8dnse`pd9-,,!oeva9._khkn9cn]u+: Naokqn_a68ol]jopuha9bkjp)saecdp6^kh`7 e`9H]^ah?kjpnkhE`:8+ol]j:"j^ol7kj"j^ol7 8ol]jopuha9bkjp)saecdp6^kh`7 e`9H]^ahL]caE`: 8+ol]j: 8^n+: 8dnse`pd9-,,!oeva9._khkn9cn]u+: Pkkhpel68^n+: 8]ol6Patp>ktnqj]p9oanranE@9Patp>ktPkkhpel PatpIk`a9OejchaHejaSe`pd93.,lt Reoe^ha9pnqa:8+]ol6Patp>kt: 8ol]jopuha9`eolh]u6jkjae`9AnnknIaoo]ca: AnnknIaoo]ca$Iaoo]caejcnkql(Patp9Iaoo]capatp% 8^n+: 8]ol6Patp>ktnqj]p9oanranE@9Patp>ktAnnknIaoo]ca PatpIk`a9OejchaHejaSe`pd93.,lt Reoe^ha9pnqa:8+]ol6Patp>kt: 8+ol]j: 8dnse`pd9-,,!oeva9._khkn9cn]u+: 8]ol6?da_g>ktnqj]p9oanranE@9?dg>ktReoe^ha Patp9I]gaahaiajpreoe^ha+: 8dnse`pd9-,,!oeva9._khkn9cn]u+: 8`ere`9patp>kt?kjp]ejanopuha9reoe^ehepu6de``aj: 8]ol6Patp>ktnqj]p9oanranE@9A`epPatp>kt PatpIk`a9IqhpeHeja Daecdp90,,ltSe`pd93.,lt Reoe^ha9pnqa:8+]ol6Patp>kt: 8+`er: 8^n+: 8p]^ha^kn`an9,se`pd9-,,!: 8pn: 8p`]hecj9habpse`pd94,!: 8h]^ah: 8ejlqpe`9o]raIa?hkoa?da_g^kt pula9_da_g^ktr]hqa9?hkoa+: ?hkoa]bpanO]ra 8+h]^ah: 8+p`: 8p`]hecj9necdpse`pd9.,!: 8ejlqppula9^qppkj kj_he_g9o]raIa$%7napqnjb]hoa7r]hqa9O]ra+:"j^ol7 8ejlqppula9^qppkj kj_he_g9de`aA`epkn$%7napqnjb]hoa7r]hqa9?hkoa+: 8+p`: 8+pn: 8+p]^ha: 8^n+: 8`ere`9Iaoo]caH]^ahopuha9_khkn6Cnaaj7 bkjp)saecdp6^kh`: 8+`er: 8^n+: 8ol]jopuha9_khkn6Na`:8opnkjc:Dejp68+opnkjc:± Nabnaodl]ca^u_dkkoejc]jkpdanh]jcq]ca8+ol]j: 8+p`:
241
242
C H APT ER 5 N EXT ENDING THE R ES OU R C E MODEL
8+pn: 8+p]^ha: 8+p`: 8+pn: 8+p]^ha: 8+`er: 8o_nelph]jcq]ca9f]r]o_nelppula9patp+f]r]o_nelp: sde_d@kc9eoEA;`k_qiajp*]hh*pdaH]uan6`k_qiajp*capAhaiajp>uE`$pdaH]uan%7 oapPeiakqp$#ejepA`epkn$%#(-,,%7 eb$pulakb$Ouo%99qj`abeja`%Ouo*=llhe_]pekj*jkpebuO_nelpHk]`a`$%7 8+o_nelp: This is simply a fragment of HTML containing the controls and the supporting script that man AGESTHEPAGELOADINGCYCLE4HECONTROLHASAFEWTRIVIALLINESOFCODE BEHIND,ISTING !S written, everything relies on JavaScript in the client. The Web Service on the server acts as an access layer to the underlying resource provider. Listing 5-23. The “code-behind” for the Control qoejcOuopai7 qoejcOuopai*Sa^*QE7 lq^he_l]npe]h_h]ooa`ep6Ouopai*Sa^*QE*Qoan?kjpnkh w lnkpa_pa`kranne`arke`KjEjep$Arajp=ncoa% w L]ca*Aj]^haArajpR]he`]pekj9b]hoa7 ^]oa*KjEjep$a%7 y lnkpa_pa`kranne`arke`KjHk]`$Arajp=ncoa% w ^]oa*KjHk]`$a%7 y lnkpa_pa`opnejcPdaia w cap w napqnjOaooekjWpdaiaY]oopnejc7 y y y The only public parameter we need is the currently selected theme. This value is included in the Web Service calls, where it is used to route the resource data into the right resource storage.
C H A P T E R 5 N E X T E N D I N G T H E R E S O U R C E M O D E L
Summary In this chapter, we looked closely at how to handle resource providers. A complete solution is capa BLEOFCHANGINGINTERNALBEHAVIORWITHOUTLOSINGANYDEFAULTFEATURES ANDINCLUDESADESIGN TIME CONTROL$ESIGN TIMESUPPORTISANIMPORTANTPARTOFANYCUSTOMSOLUTION ANDITWOULDBESHORT sighted to ignore the option of using designer tools (even if you do not intend to employ them YOURSELF !LLCODESHOWNINTHISCHAPTERHASFULLDESIGN TIMESUPPORT INCLUDINGTHESTEPSFOR enabling debugging. &URTHERMORE WEMADETHERESOURCEPROVIDERACCESSIBLEVIAA7EB3ERVICE7EBUILTA*AVA3CRIPT BASEDEDITORONTHECLIENTTOREADANDSAVERESOURCESATRUNTIME ENABLINGRESOURCE BASEDPAGESTOBE editable online. The examples demonstrate how to implement a highly customized solution, which fits well into the existing ASP.NET framework.
243
C HAPTER
6
Page and Session Management
T
he Hypertext Transfer Protocol (HTTP) is the reason for the success of the Internet. It is also the source of the drawbacks in the usability of applications. To overcome these limitations, ASP.NET provides several ways of maintaining the state of pages—and the controls between postbacks— throughout the whole session. Whatever the type of data existing within an application, there are two things to consider: the storage of application data on the server, and how to integrate the user’s session with the application data. In this chapter, I’ll explain how to customize in both of these areas for your real-life applications. In particular, you’ll find information about:
s 4HEPAGEPERSISTERTECHNIQUE
s 7RITINGACUSTOMPAGEPERSISTER
s 4HEINTERNALSOFTHESESSIONSTATEPROVIDER
s 7RITINGACUSTOMSESSIONSTATEPROVIDER
The approach presented here is based on the provider model as explained in Chapter 4. Please refer back for useful information about providers, their purpose, and how they fit into the ASP.NET framework.
The Page State Persister Handling active content and creating rich internet applications are difficult to integrate. Page persistence is the key to overcoming this challenge, and a good understanding of this feature is the basis of creating complex applications.
A Look Back The protocol spoken between server and browser is HTTP, and there are many books, articles, blogs, and newsgroup posts complaining about its limitations. However, HTTP has succeeded mainly BECAUSEOFITSSIMPLICITY4HEREARETHREEKEYPROPERTIESWHICHMAKE(440UNIQUE
s )TUSESTEXTTOTRANSPORTBOTHMESSAGESANDCONTENT
s )TRUNSONTOPOF4#0
s )TISASTATELESSPROTOCOL.
245
246
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
h3TATELESSvCANBETHOUGHTOFAShFIREANDFORGETv4HECLIENTINITIATESAREQUESTTOTHESERVERTHE SERVERPROCESSESTHEREQUESTANDRESPONDSWITHTHEANSWER!FTERTHAT THESERVERFORGETSALLABOUT the procedure. Once the conversation has ended, nothing is left for further processing—at least, this is the case for the protocol.
The Default Page State Persister The statelessness of HTTP means that there is no formal way of remembering, either on the client or the server, the state of any past actions involving the same user. However, every web development environment provides some means of recognizing session states and storing user data. ASP.NET is no exception, and as for many other parts of the .NET Framework, there are several ways of achieving this.
State Storage in ASP.NET Since all stateMANAGEMENTAPPROACHESREQUIRECUSTOMCODINGBYAPPLICATIONDEVELOPERS ITSWORTH looking into them in more detail. Understanding the pros and cons of each method makes it easier to decide which one to use, where to customize it, and how to get the most out of it. ASP.NET can store information about the page’s state in several collections:
s Cookies: You can create private cookies and store them on the client’s machine in order to obtain information from them. This is not the best method, but it’s the default method.
s View State: ASP.NET controls, as well as several other parts, use view state to store control-specific information about the previous state. We’ll discuss all facets of view state later in the section “Persisting Page State Information.”
s Session: This is a collection of key-value pairs related to the current user’s session. It’s a way of adding more information, on the server side, to a session state stored elsewhere (in a cookie, for instance).
The Application collection is another set of key-value pairs that retain application-level data SHAREDAMONGALLUSERS)NCONTRAST THE#ONTEXTCOLLECTIONISLIMITEDTOTHECURRENTREQUEST RESPONSE flow—data in the Context collection does not survive the page cycle. Cookies, View State and the Session collection give the developer the illusion of stateful page handling. Most commonly, they will use a single cookie, called the session cookie, to store an ID with the shortest lifetime possible in the browser’s cookie memory, just until the session ends. 7HENTHECLIENTMAKESANEWREQUEST THECOOKIEISRETURNEDINTHEREQUEST4HESERVERDECODESTHE )$TODETERMINEWHICHBROWSERINSTANCEANDHENCE WHICHUSER ISSENDINGTHEREQUEST As an experienced developer, you might already know how to use the Cookie, Session, and Application collections. Each of them has a different storage mechanism and must be used wisely. You may remember that there are many code snippets scattered throughout the page’s code which OBTAINACCESSTOTHEINFORMATIONWHENEVERITISREQUIRED(OWEVER THISISNOTIDEAL ASITDETRACTS from the declarative nature of ASP.NET pages. Not only does it make pages harder to read, but it could lead to bugs if overused.
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
Persisting Page State Information 4HEFIRSTSTEPINMANAGINGSTATEDATAISTOMASTERTHETECHNIQUESFORRETAININGTHEPAGESTATETHROUGH each postback. The information on the “current” page is generally stored within several controls. Therefore, we’ll need a way of storing the controls’ state information. The state storageRELIESONTWOSIMILARTECHNIQUESTHEVIEWSTATEANDTHECONTROLSTATE4HE internal nature and pitfalls of the view state were described in Chapter 1. Here, I’ll show how to change the storage and retrieval mechanism.
View State Explained Recall the purpose of view state: to store data about the properties of controls that are set programmatically. View state is not responsible for the sticky form behavior that retains the controls’ current state according to the user’s selection. The control state holding this information is retrieved from the form’s data, which is posted back to the server during a normal postback. Additional information is stored in a special hidden field. Internally, view state uses an object of type Op]pa>]c. It retains data and includes code that serializes the data into text format and stores it in the hidden field. Consider using a H]^ah control, where you set the color to turn red if an error occurs. You’ll want to maintain this state until the error has been corrected, without checking and setting the property repeatedly. Once set programmatically, view state stores this value in the Op]pa>]c and persists it in the page via a hidden field. When the page is initialized after a postback, the value is RETRIEVEDANDASSIGNEDTOTHEPROPERTYNOCUSTOMCODEREQUIRED Clearly, view state plays a crucial role in page development, and can be used internally to store a control’s state.
Control State Explained View state and control state are related and therefore need to be discussed together. Whereas view STATECONTAINSPROGRAMMATICALLY SETPROPERTYVALUES CONTROLSTATECONTAINSTHEDATAREQUIREDFOR one control to make it appear after a postback in the same state that it was in beforehand. Frustratingly, the control state is stored as part of the view state data by default. This means that there are two different ways of saving control data during postbacks, but both use the same storage—the hidden view state field. However, control developers may decide to write the control state into private hidden fields to retain the state and decouple view state and control state storage, then.
NNote
Control state is only for small amounts of critical data that are essential for the control across postbacks. It is not seen as an alternative to view state.
247
248
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
In fact, there is no way to treat both states separately from each other from page-level code. Disabling the view state suppresses the serialization and storage mechanism entirely, so the control state gets lost. Complex controls, such as Cne`Reas, store information about paging and sorting, because there are no corresponding HTML fields able to store and send such data as part of a regular form. The Cne`Reas has several indices that retain the internal state. Changes to underlying data are tracked and used to fire appropriate events. These are properties such as A`epEj`at, L]caEj`at, and Oknp@ena_pekj. Several internal actions might lead to changes that need to be stored during postbacks, but this does not use the same path through the code as the view state would use for public properties. The view state data stored in the hidden field would expand, and developers using your control might by concerned by the growing page and form data. Control developers should be careful about using the control state because of its dependency on view state. A disabled view state could cause the control state to not function properly. However, as a control developer, you have several options for managing the way your control stores internal state values. First, the L]ca*NaceopanNamqenao?kjpnkhOp]pa method must be called to ensure that the control starts storing the control state. Apart from within view state, there is no Op]pa>]c that stores values directly. It’s up to you to implement the whole storage level. This is both GOODANDBAD ASALTHOUGHYOULLHAVEMORECONTROLOFWHATHAPPENSINTERNALLY ITREQUIRESMORE effort to store a few primitive values. Listing 6-1 shows a simple control that stores several values in the control state. Listing 6-1. A Custom Control Using the Control State lq^he__h]ooIu?kjpnkh6?kjpnkh w opnejcop]pa@]p]7 lnkpa_pa`kranne`arke`KjEjep$Arajp=ncoa% w L]ca*NaceopanNamqenao?kjpnkhOp]pa$pdeo%7 ^]oa*KjEjep$a%7 y lnkpa_pa`kranne`arke`Hk]`?kjpnkhOp]pa$k^fa_po]ra`Op]pa% w k^fa_pWY_pnhOp]pa9$k^fa_pWY%o]ra`Op]pa7 ^]oa*Hk]`?kjpnkhOp]pa$_pnhOp]paW,Y%7 op]pa@]p]9$opnejc%_pnhOp]paW-Y7 y lnkpa_pa`kranne`ak^fa_pO]ra?kjpnkhOp]pa$% w k^fa_pWY_pnhOp]pa9jask^fa_pW.Y7 _pnhOp]paW,Y9^]oa*O]ra?kjpnkhOp]pa$%7 _pnhOp]paW-Y9op]pa@]p]7 napqnj_pnhOp]pa7 y y
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
If you plan to write a custom page state persister, you’ll have to handle the control state’s data ASWELLASTHEVIEWSTATESDATA4HEPERSISTERSPROVIDERMUSTSTOREBOTHOTHERWISEALMOSTALLCUStom controls will stop functioning properly. An alternative is to store the values your own way—using private hidden fields, perhaps—in order to remain independent of the provider. However, this is beyond the scope of this chapter. Besides, adding infrastructure logic to custom controls runs counter to the ASP.NET paradigm, in which the base framework contains and exposes a rich infrastructure. In conclusion, view state and control state are stored in the same place in order to completely and automatically restore a control’s properties. Since the storage and management of view state and control state are related, I’ll discuss them together in this chapter. When I use the term “view state” consider it as including control state as well.
The Default Providers ASP.NET uses a provider to manage storage of the view state. The preceding description assumes the usage of one of the two providers’ available out-of-the-box. Both providers derive from an abstract base class, L]caOp]paLanoeopan:
s De``ajBeah`L]caOp]paLanoeopan
s OaooekjL]caOp]paLanoeopan
By default, the De``ajBeah`L]caOp]paLanoeopan is used to store the view state in a hidden field. When you look into a page’s source code in the browser, you’ll find a field with the name __VIEWSTATE. This is the serialized, encoded, and optionally encrypted collection of properties stored in the Op]pa>]c (see Figure 6-1).
Figure 6-1. Typical view of the view state’s hidden field
249
250
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
This is what you’ll see if the De``ajBeah`L]caOp]paLanoeopan is used. There are several pros and cons to using this method. Many pages store information, and many users have individual data. The state is associated with the current page, and apart from session data that is independent of the current page, this data is specific to the page and the user. Storing the data on the client computer MOVESTHEMEMORYREQUIREDFROMTHESERVERTOTHECLIENT4HISCOSTSBANDWIDTH BUTSAVESSERVER resources. If you have 1KB of session data and 1,000 users accessing the server simultaneously, you’ll need to store 1MB of data. If this happens for 100 pages, you’ll need to store 100MB. This isn’t usually significant, but growing traffic will increase the resource consumption accordingly. For this reason, using the browser’s memory is a better option for scalability, and this is why the default provider uses the De``ajBeah`L]caOp]paLanoeopan option. However, sometimes it’s not the server’s resources that cause the bottleneck, but the bandWIDTH PARTICULARLYONMOBILEDEVICES6IEWSTATEISTRANSMITTEDTWICEFOREACHREQUEST!CCESSING a dozen pages with 1KB of session data each would force the device to transfer and process an additional 24KB of data, and this could be even higher for badly-developed pages. To investigate the contents of the view state, a tool such as Fritz Onion’s ViewStateDecoder is invaluable. Knowing what is stored in view state makes it’s easier to design a custom page state persister as shown in Figure 6-2. You can find the most recent version of ViewStateDecoder at: dppl6++sss*lhqn]hoecdp*_ki+_kiiqjepu+ia`e]+l+1-244*]olt.
Figure 6-2. Examine the contents of the view state. At this point, the particular contents of the view state aren’t important. Decoding the data is merely to understand the internal behavior.
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
Changing the Default Provider The alternative provider, OaooekjL]caOp]paLanoeopan, moves the storage strategy from the client to the server. The data is stored in the session, along with other session variables. However, view state data is not session data—merely the state of controls. View state for each page is stored separately in this provider. To change from the default provider, you must override the L]caOp]paLanoeopan property of the L]ca class: lnkpa_pa`kranne`aL]caOp]paLanoeopanL]caOp]paLanoeopan w cap w napqnjjasOaooekjL]caOp]paLanoeopan$pdeo%7 y y It’s also possible to set this globally by assigning the following attribute to the l]cao tag in the web.config file. 8Ouopai*sa^: 8l]caol]ca>]oaPula9Lanoeopan>]oaL]ca: *** 8+Ouopai*sa^: This assigns a base page to all pages—PersisterBasePage, for example, which derives from the L]ca class and overrides the L]caOp]paLanoeopan property. However, this prevents you from using code-behind. You could set the base class individually for each page. This is more flexible, but more demanding. The page’s definition could look like the following: lq^he_l]npe]h_h]oo[@ab]qhp6Lanoeopan>]oaL]ca As easy as this seems, it always involves a compromise. The workload is either on the client or the server. Fortunately, the extensibility model of ASP.NET allows you to create custom providers to transparently replace the existing ones.
Developing a Custom Page State Provider As already mentioned, the entry point into a custom provider is the implementation of an abstract BASECLASS"EFOREYOUSTARTCODING YOULLNEEDTOANSWERTWOQUESTIONS
s 7HERESHALL)STOREDATADURINGPOSTBACKS
s (OWDO)IDENTIFYWHENDATAISNOLONGERCURRENT
The first answer is easy. Data is typically stored in a file system, a database, or in memory. The second answer is more complicated. Data is related to a particular page, and when a user leaves the page, the data becomes redundant. This event is difficult to recognize. The user can leave the page by navigating to another page within the same application, by jumping to a different Web site on another server, or even by simply closing the browser. Keeping the data in storage forever is not an option, as this would increase the storage space used and the effort of retrieving the right DATA7HILETHEDEFAULTPROVIDERISNOTTHEIDEALSOLUTIONEITHER ITDOESANSWERTHESECONDQUESTION if the user moves on to another page, the browser removes the data from the client’s memory. Thus, you don’t need to worry about obsolete data.
251
252
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
4HEREISNOSATISFACTORYANSWERTOTHISQUESTION)TSUSUALLYSUFFICIENTSIMPLYTOREMOVEOUTdated data that has not been refreshed for a specific period of time. This assumes that a user is no longer connected to the server. Selecting a value corresponding to the session timeout will be ADEQUATE for most applications.
Choosing the Data Storage In this example, I’ll make the storage as easy as possible to implement. The data is stored on the server in the file system. This could even be a shared drive, if you need to support a web farm. File systems are highly efficient for storing and retrieving data and avoid dependency on a database. However, this is simply an example of how to extend the page state persister. The storage solution you use depends entirely on the characteristics of your application. You’ll notice that the hidden field is still in charge, because it contains some information for IDENTIFYINGUNIQUEPAGES4HISISWHYTHEHIDDENFIELDISNOTEMPTYWHENUSINGTHEDEFAULTPROVIDER even if the view state is disabled. However, the identifier only needs a few bytes, and shouldn’t affect BANDWIDTHREQUIREMENTS
Analyzing a Provider Before implementing the provider from scratch, let’s examine the OaooekjL]caOp]paLanoeopan class for firsthand information about the internal structure of such a provider. This class stores data on the server, just as the custom provider is intended to do. If you investigate the view state value captured from the hidden field, you’ll see a simple hexadecimal number, such as 8cb5cd5d086eee0. 4HISISTHEHEXADECIMALREPRESENTATIONOFTHEPROCEDURE5SINGTHEFOLLOWINGCODESEQUENCE YOU can decode the value: jas@]paPeia$,t4_^1_`1`,42aaa,(@]paPeiaGej`*Hk_]h%*PkOpnejc$% This value decodes to 14 February 2009, 4:09pm and 57 seconds. The OaooekjL]caOp]paLanoeopan stores not one, but nine of these values internally, to keep the history of page accesses alive. When the user presses the browser’s back button, a previous page is pulled from the server. Using the timestamp as a key, the correct data is retrieved from the server’s storage. The name of the internal QUEUEIS??6)%734!4%15%5%#ONSTRAININGTHENUMBEROFPAGESINTHEHISTORYSOLVESTHESECOND QUESTIONRAISEDEARLIERANDLIMITSTHESTORAGESPACEUSED3INCETHEUSERSBEHAVIORDEPENDSON THEAPPLICATIONSNAVIGATIONSTRUCTURE YOUCANALTERTHEQUEUESIZEUSINGTHEFOLLOWINGENTRYINTHE web.config file: 8oaooekjL]caOp]padeopknuOeva9-.+: This value allows 12 pages in the history list. The value you choose is always a compromise BETWEENCONVENIENCEANDRESOURCEUSAGE)FTHEUSEREXCEEDSTHEQUEUESIZELIMIT THEVIEWSTATE becomes invalid and an exception is thrown. You can catch the exception, deliver an error page, and end the session, or simply redirect to a default page. However, as this is not an ideal solution, the following example shows an alternative way of dealing with “dead” data.
Implementing the Provider As we learned from the default implementation, the whole procedure of loading and saving the state consists of two methods, Hk]` and O]ra, respectively. Both use the fully implemented serialization and deserialization methods provided by the base class to convert between the Op]pa>]c object and a text string. The example stores the data in single files in the file system. Each page’s data becomes one file. To retrieve the values, two pieces of information must be held in the view state field: the name of the file and the timestamp. The timestamp is needed for private garbage collection to
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
remove expired data. The file name associates the page’s view state field with the file itself. After rendering to the browser, the hidden view state looks like the following: 8ejlqppula9de``ajj]ia9[[REASOP=PAE@e`9[[REASOP=PAE@± r]hqa94_^1_`1`,42aaa,).me,mlnvk`erdd11qskc/g01*ro+: The first part of the value, before the hyphen, is the timestamp. I have retained the coding schema. The second part is a randomly generated value, plus the extension, *ro. Listing 6-2 shows the code for the custom provider, as described thus far. Listing 6-2. Persisting the View State Using Local File System lq^he__h]ooBehaL]caLanoeopan6L]caOp]paLanoeopan w lner]pa_kjopopnejcReasOp]paBkniBeah`E@9[[REASOP=PAE@7 lner]pa_kjopopnejcOp]paBehaBkh`anL]pd9z+Op]pa@]p]+7
lq^he_BehaL]caLanoeopan$L]cal% 6^]oa$l% w y lq^he_kranne`arke`Hk]`$% w opnejcop]paE`ajpebeanR]hqa9± Dppl?kjpatp*?qnnajp*Namqaop*BkniWReasOp]paBkniBeah`E@Y7 eb$op]paE`ajpebeanR]hqa*Hajcpd:,% w opnejcbehaJ]ia9op]paE`ajpebeanR]hqa7 opnejcbehaL]pd9Dppl?kjpatp*?qnnajp*Oanran*I]lL]pd$± Op]paBehaBkh`anL]pd'behaJ]ia%7 opnejc_kjpajpo9Beha*Na]`=hhPatp$behaL]pd%7 L]enop]pa9^]oa*Op]paBkni]ppan*@aoane]heva$_kjpajpo%]oL]en7 eb$op]pa9jqhh% w ^]oa*ReasOp]pa9op]pa*Benop7 ^]oa*?kjpnkhOp]pa9op]pa*Oa_kj`7 y y y lq^he_kranne`arke`O]ra$% w eb$^]oa*L]ca*Bkni9jqhh± ""$^]oa*?kjpnkhOp]pa9jqhhxx^]oa*ReasOp]pa9jqhh%% w ++?na]pabehaj]iabkno]ra opnejcbehaJ]ia9Opnejc*Bkni]p$w,6t4y)w-y*ro(@]paPeia*Jks*Pe_go(± Dppl?kjpatp*?qnnajp*Oaooekj*OaooekjE@%7 opnejcbehaL]pd9Dppl?kjpatp*?qnnajp*Oanran*I]lL]pd$± Op]paBehaBkh`anL]pd'behaJ]ia%7 L]enl9jasL]en$^]oa*ReasOp]pa(^]oa*?kjpnkhOp]pa%7 Beha*Snepa=hhPatp$behaL]pd(^]oa*Op]paBkni]ppan*Oane]heva$l%%7 r]ndb9jasDe``ajBeah`
253
254
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
w R]hqa9behaJ]ia( E@9ReasOp]paBkniBeah`E@ y ^]oa*L]ca*Bkni*?kjpnkho*=``=p$,(db%7 y y y The base class provides methods of serializing and deserializing the data. You can access these methods by calling ^]oa*Op]paBkni]ppan. The provider saves both the control state and the view state, and creates a L]en from them, to group the values together. The serialized L]en is written directly into the file using Beha*Snepa=hhPatp and retrieved using Beha*Na]`=hhPatp. We are not concerned about threading here, because each storage location is dedicated to one user session and one page. There’s a chance that a user could send the same page twice to the server at the same moment, but that’s unlikely—and out of our scope. The Hk]` method is the reverse of the O]ra method, using the same approach. The O]ra method creates a hidden field using the De``ajBeah` control and adds it to the beginning of the current control collection.
Extending the Provider Although, from this example, custom implementation seems very simple, this isn’t entirely true. Over time, the folder for state files accumulates more and more files. You might consider deleting session state files triggered by the session end event, but that’s not reliable. The event is fired only if the session ends by command. If the user closes the browser the session dies “silently,” and only the session timeout can be used to remove remaining objects. One option is to remove old files regularly, but using such a cleanup process for each page REQUESTWOULDINCREASETHENUMBEROFFILEACCESSESANDTHUSDECREASEPERFORMANCE!BETTER APPROACHISTOINCLUDEAREGULARFOLDERCHECKINDEPENDENTOFANYREQUESTS5SINGTHISTEST YOUCAN delete all files over a certain age. Implementing the tidy-up code in the provider is undesirable. Recall the goals and intended behavior of a provider: it’s not a good idea to have them running basic tasks. Rather, the health monitoring system provided by ASP.NET is ideal for managing the “health” of the provider’s storage.
Maintaining the Storage by Using the Health Monitor Since the cleaning-up procedure must be run regularly, the Sa^Da]np^a]pArajp is an ideal trigger. This event fires at regular intervals, invoking another provider. The health monitoring system itself is based on a provider model. Therefore, the second part of a custom view state storage solution consists of a “custom cleanup provider.” Before you implement this provider, take a look at the configuration step needed in the web. config file in the 8ouopai*sa^: section (see Listing 6-3). Listing 6-3. Activating the Health Monitor 8da]hpdIkjepknejcaj]^ha`9pnqada]np^a]pEjpanr]h91: 8lnkre`ano: 8]``j]ia9Beha?ha]jqlLnkre`an pula9=lnaoo*Atpajoe^ehepu*ArajpLnkre`an*Beha?ha]jqlLnkre`an(± =lnaoo*Atpajoe^ehepu*ArajpLnkre`an+: 8+lnkre`ano: 8arajpI]llejco: 8]``j]ia9Beha?ha]jqlArajppula9Ouopai*Sa^*I]j]caiajp*Sa^Da]np^a]pArajp op]npArajp?k`a9,aj`Arajp?k`a9.-0304/203:8+]``:
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
8+arajpI]llejco: 8nqhao: 8]``j]ia9?ha]jqlArajparajpJ]ia9Beha?ha]jqlArajp lnkre`an9Beha?ha]jqlLnkre`an+: 8+nqhao: 8+da]hpdIkjepknejc: The da]hpdIkjepknejcELEMENTREQUIRESTHREECHILDELEMENTS
s lnkre`an: The provider that issues the action
s arajpI]llejc: The event that invokes the action
s nqhao: A relation between a specific event and a provider
The event is based on the Sa^Da]np^a]pArajp class. Enable the monitor using the aj]^ha`9pnqa attribute. The da]np^a]pEjpanr]h91ATTRIBUTESETSTHEHEARTBEATTOFIVESECONDS this is just for testing and debugging purposes. In real-life scenarios, set a value that provides a compromise between memory consumption and cleanup effort in order to monitor the number of files stored in the data folder. A heartbeat interval of one hour is a good starting value. The example provider configured here is called the Beha?ha]jqlLnkre`an. Listing 6-4 demonstrates its implementation. Before beginning, you’ll need to know a few things about health monitoring providers. The monitoring system is implemented to observe an ASP.NET application. It must be active before the application starts and after it ends, and it must exist independently of CURRENTREQUESTS THECURRENTREQUESTSSTATEINTHEPIPELINE AND))34HEREFORE THECLASSMUSTBE implemented in an assembly that can be loaded separately. Adding the class to App_Code or any place in your current project will fail. Instead, create a simple class library project, reference it from the Web application you wish to monitor, and add the following class to that project. 4HECODEREQUIRESTHEOuopai*Sa^*I]j]caiajp namespace to be available. Listing 6-4. Simple Health Monitor Provider That Tidies Up a Folder lq^he__h]ooBeha?ha]jqlLnkre`an6Sa^ArajpLnkre`an w lner]pa_kjopopnejcOp]paBehaBkh`anL]pd9Op]pa@]p]+7 lq^he_Beha?ha]jqlLnkre`an$% 6^]oa$% w y lq^he_kranne`arke`Bhqod$% w ++jkpnamqena` y lq^he_kranne`arke`Lnk_aooArajp$Sa^>]oaArajpn]eoa`Arajp% w @]paPeia`pN]eoa`9n]eoa`Arajp*ArajpPeia7 ++Naikrabehao opnejcbehaL]pd9L]pd*?ki^eja$DpplNqjpeia*=ll@ki]ej=llL]pd(± Op]paBehaBkh`anL]pd%7 bkna]_d$opnejcbehaej@ena_pknu*CapBehao$behaL]pd%% w eb$`pN]eoa`)Beha*Cap?na]pekjPeia$beha%:PeiaOl]j*BnkiDkqno$2%%
255
256
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
w Beha*@ahapa$beha%7 y y y lq^he_kranne`arke`Odqp`ksj$% w ++?ha]jqlkjodqp`ksj opnejcbehaL]pd9L]pd*?ki^eja$DpplNqjpeia*=ll@ki]ej=llL]pd(± Op]paBehaBkh`anL]pd%7 bkna]_d$opnejcbehaej@ena_pknu*CapBehao$behaL]pd%% w Beha*@ahapa$beha%7 y y y Each heartbeat of the monitor invokes the Lnk_aooArajp method. A simple loop locates all the files exceeding the specified age—six hours in the example—and deletes them. The file expiry test can be extended to suit your needs. You could check if the corresponding session is still alive, or if the data in the file matches certain conditions. The current timestamp is passed to the method with the n]eoa`Arajp*ArajpPeia value. The path to the application can be retrieved using the DpplNqjpeia object. Remember that Dppl?kjpatp might not be available, because the first heartbeat arrives when the application has not yet begun. Other monitoring events could provide the Dppl?kjpatp object, but basic events such as the Sa^Da]np^a]pArajp don’t support this.
Conclusion In this section, we looked at replacing the default page state persister provider that is responsible for storing view state and embedded control state information. Using the health monitoring system with another custom provider allows us to maintain cleanup routines. Page states are not the only state persisted during a user’s session. Data is also stored in session variables, which are, by default, stored in memory. However, as in several other parts of ASP.NET, the session state is built on the provider model. The next section explains this in detail and shows how to extend the behavior using a custom provider.
Session State Providers As shown in Chapter 4, providers are responsible for allowing storage access to services. The session state module is one such service relying on several out-of-the-box storage options. For all available options, a dedicated provider exists. You can also add your own provider to implement a fully customized storage strategy.
The Session State Service The session state service is defined in the Ouopai*Sa^*OaooekjOp]pa*OaooekjOp]paIk`qha. Each instance creates a module that in turn creates a space to store data. The data is—as the name implies—associated with the user’s session. Ouopai*Sa^*OaooekjOp]pa*OaooekjOp]paOpkna@]p] is the class that defines the storage space. One instance is created for each user session. The OaooekjOp]paOpkna@]p] class is responsible for serializing and deserializing the session data.
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
The serialization uses a highly efficient binary format partly customized with hardcoded transformations for scalar values and the >ej]nuBkni]ppan type for all other values. This type is defined in the Ouopai*Nqjpeia*Oane]hev]pekj*Bkni]ppano*>ej]nu namespace. Since the session serializes all objects, the process includes two types of data: static and non-static. Non-static data are held in an Epaio collection. This collection is defined as an interface of type EOaooekjOp]paEpai?khha_pekj, which is a simple collection based on E?khha_pekj and EAjqian]^ha. It’s exposed by the Oaooekj object through an indexer and the Epaio property. The Op]pe_K^fa_po property relies on the DpplOp]pe_K^fa_po?khha_pekj type, which in turn is a collection that implements the same interfaces. However, the serialization is implemented slightly differently here. For both Epaio and Op]pe_K^fa_po, you can call the Oane]heva and @aoane]heva methods to translate the data to and from a storable format. Apart from the two types of data previously mentioned, the data store has its own Peiakqp PROPERTY4HISISNECESSARY ASTHESERVICEISIMPLEMENTEDASAN(440MODULEANDRELIESONREQUEST events. If the final event is not raised, the store would become an orphan if the timeout did not terminate it. Internally, the session state service class is defined as follows: lq^he_oa]ha`_h]ooOaooekjOp]paIk`qha6EDpplIk`qha The session state module is executed before the handlers in the =_mqenaNamqaopOp]pa pipeline state. This ensures that session data is already present when the page’s handler is loaded. The modULEATTEMPTSTORETRIEVETHESESSION)$FROMTHEREQUESTANDUSESTHISTOOBTAINDATAFROMTHESESSION store provider. If both function correctly, the session data is rebuilt as a dictionary and exposed to the DpplOaooekjOBJECT4HESESSIONDATAISSTOREDATTHEENDOFTHEREQUESTPROCESSING WITHINTHE Naha]oaNamqaopOaooekj stage of the pipeline. If no session ID is present, a new one is created. If the ID is there and some session data is changed, the provider is used to store these data. The service’s providers use the OaooekjOp]paOpknaLnkre`an>]oa provider base class, which defines a subset of methods all providers share. As shown in Listing 6-5, this is an embedded class used here as a basis for custom providers. Listing 6-5. The OaooekjOp]paOpknaLnkre`an>]oa Is the Base for All Session State Providers W=olJapDkopejcLanieooekj$Oa_qnepu=_pekj*Hejg@ai]j`(± Harah9=olJapDkopejcLanieooekjHarah*Iejei]h%( =olJapDkopejcLanieooekj$Oa_qnepu=_pekj*Ejdanep]j_a@ai]j`(± Harah9=olJapDkopejcLanieooekjHarah*Iejei]h%Y lq^he_]^opn]_p_h]ooOaooekjOp]paOpknaLnkre`an>]oa6Lnkre`an>]oa w lnkpa_pa`OaooekjOp]paOpknaLnkre`an>]oa$% w y lq^he_]^opn]_pOaooekjOp]paOpkna@]p]?na]paJasOpkna@]p]$Dppl?kjpatp_kjpatp(± ejppeiakqp%7 lq^he_]^opn]_prke`?na]paQjejepe]heva`Epai$Dppl?kjpatp_kjpatp(± opnejce`(± ejppeiakqp%7 lq^he_]^opn]_prke`@eolkoa$%7 lq^he_]^opn]_prke`Aj`Namqaop$Dppl?kjpatp_kjpatp%7 lq^he_]^opn]_pOaooekjOp]paOpkna@]p]CapEpai$Dppl?kjpatp_kjpatp(± opnejce`(± kqp^kkhhk_ga`(± kqpPeiaOl]jhk_g=ca(± kqpk^fa_phk_gE`(± kqpOaooekjOp]pa=_pekjo]_pekjo%7
257
258
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
lq^he_]^opn]_pOaooekjOp]paOpkna@]p]CapEpaiAt_hqoera$Dppl?kjpatp_kjpatp(± opnejce`(± kqp^kkhhk_ga`(± kqpPeiaOl]jhk_g=ca(± kqpk^fa_phk_gE`(± kqpOaooekjOp]pa=_pekjo]_pekjo%7 ejpanj]hrenpq]hrke`Ejepe]heva$opnejcj]ia(± J]iaR]hqa?khha_pekj_kjbec(± EL]npepekjNaokhranl]npepekjNaokhran% w y lq^he_]^opn]_prke`Ejepe]hevaNamqaop$Dppl?kjpatp_kjpatp%7 lq^he_]^opn]_prke`Naha]oaEpaiAt_hqoera$Dppl?kjpatp_kjpatp(± opnejce`(± k^fa_phk_gE`%7 lq^he_]^opn]_prke`NaikraEpai$Dppl?kjpatp_kjpatp(± opnejce`(± k^fa_phk_gE`(± OaooekjOp]paOpkna@]p]epai%7 lq^he_]^opn]_prke`NaoapEpaiPeiakqp$Dppl?kjpatp_kjpatp(opnejce`%7 lq^he_]^opn]_prke`Oap=j`Naha]oaEpaiAt_hqoera$Dppl?kjpatp_kjpatp(± opnejce`(± OaooekjOp]paOpkna@]p]epai(± k^fa_phk_gE`(± ^kkhjasEpai%7 lq^he_]^opn]_p^kkhOapEpaiAtlena?]hh^]_g$± OaooekjOp]paEpaiAtlena?]hh^]_gatlena?]hh^]_g%7 y Inspecting the CapEpai and NaikraEpai methods, you’ll notice that the provider is responsible for creating (via the kqp parameter for CapEpai) the session-specific OaooekjOp]pa@]p]Opkna. The vital parameter is Dppl?kjpatp ASITPASSESALLTHEINFORMATIONREQUIREDFORIDENTIFYINGTHESESSION However, the base class is abstract. In order to understand the code and learn how to implement a custom provider, we’ll need to examine certain implementations. The next sections will cover the nature of sessions and cookies that are tightly coupled with the session provider’s behavior.
Identifying the Session Despite storage management, the session state service needs to retrieve the session ID. The first attempt to overcome this stateless behavior led to the invention of cookies.
Cookies Make HTTP Taste Good . . . A cookie is a small piece of text-based information stored in the browser’s memory or disk space. It’s under the browser’s control, and it exists to send information about the state back to the server. 7HENTHEBROWSERREQUESTSANEWRESOURCE ITSEARCHESFORCOOKIESSTOREDBYTHISSPECIFICSERVERAND returns this information. The server recognizes the information, reading it back from the headers of THE(440REQUEST ANDUSESITTO obtain the client’s state.
. . . and Bad However, cookies have some serious drawbacks. If a server sends portions of a page only, this very portion would send the information back to this server and not the one serving the main page. An advertising banner is an example of such a section. At the end of the last century, the company
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
DoubleClick (eventually bought out by Google) invented a business concept based on cookies. Once a banner ad appears, the cookies related to it are placed in the browser’s memory. When the user moves to a page drawn from another server, but containing a banner pulled from the original ad server, the ad server’s code would receive the same cookie back. The original server knows that the user has been on different pages, and it also recognizes where these pages originated. The cookie makes it possible for the ad server to trace the user and track his or her behavior. By the third page, the banner ad has not been selected at random. Based on the user’s behavior, the ad server chooses the banner that best fits the user’s interests. This leads to a much higher click rate, which in turn results in more money made. This is the concept invented by DoubleClick—with which Google now generates a lot of revenue. It is as simple as it sounds. However, it has faced criticism in that it compromises the user’s privacy. The ad server knows much more of a user’s behavior than people would tell strangers voluntarily. Since cookies work silently, the user is never asked to accept or deny them. Even if modern browsers managed cookies well, it’s still a complicated task to deal with them, erasing them where appropriate and retaining them if harmless.
Beyond Cookies Avoiding the usage of cookies or even limiting them to just one—to store the session state—would give your users the opportunity to operate within a restrictive cookie usage policy. It is an advantage for a site avoiding cookies. However, it makes your life as a developer much more difficult because cookies simplify the session handling. Fortunately, ASP.NET provides a number of sophisticated TECHNIQUESTHATHELPYOUHANDLETHEDRAWBACKSOF(440SSTATELESSNESS)NTERMSOFTHESESSION state, you can decide whether to work with session cookies or without cookies at all. !30.%4MAINTAINSTHECOOKIELESSSESSIONSTATEBYAUTOMATICALLYINSERTINGAUNIQUESESSION)$ into the URL. This would look like this: dppl6++sss*]lnaoo*_ki+ $O$hep/fg.2p,1v20r-0rhi,0o,5%%+`ab]qhp*]olt. The following configuration in the web.configFILEISREQUIRED 8_kjbecqn]pekj: 8ouopai*sa^: 8oaooekjOp]pa_kkgeahaoo9pnqanacajan]paAtlena`OaooekjE`9pnqa+: 8+ouopai*sa^: 8+_kjbecqn]pekj: A session ID will eventually expire when the session ends, or has been timed out. In this case, THE)$ISRECYCLEDALATERSESSIONCOULDHAVETHESAME)$)FA52,ISSTOREDSOMEWHEREONTHE client—such as within the favorites folder, sent by email, or gathered by a search engine—this could cause a security hole, as the older session ID located in the URL could be the same as that of another current session. The nacajan]paAtlena`OaooekjE`9pnqa attribute reduces that risk by forcing it to create a new ID every time. If this behavior is not sufficient for your application, you can write your own session ID module. For complete control, consider implementing the EOaooekjE@I]j]can interface. To just control the creation of the ID, inherit from the OaooekjE@I]j]can base class. Both types are defined in the Ouopai*Sa^*OaooekjOp]pa namespace. The next example demonstrates how to replace the session ID with a custom value (see Listing 6-6). Listing 6-6. Simple Session ID Manager with Custom ID Creation j]iaol]_a=lnaoo*OaooekjOp]pa*?k`a w lq^he__h]ooOeilhaOaooekjE@I]j]can6OaooekjE@I]j]can w
259
260
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
lq^he_kranne`aopnejc?na]paOaooekjE@$Dppl?kjpatp_kjpatp% w napqnjCqe`*JasCqe`$%*PkOpnejc$%7 y lq^he_kranne`a^kkhR]he`]pa$opnejce`% w pnu w Cqe`paopCqe`9jasCqe`$e`%7 eb$e`*Amq]ho$paopCqe`*PkOpnejc$%%% w napqnjpnqa7 y y _]p_d w y napqnjb]hoa7 y y y The only methods to override are ?na]paOaooekjE@ and R]he`]pa. However, the basic form of the ID, such as $O$***%%, is still used. To obtain completely different behavior, you must implement the interFACE WHICHREQUIRESSEVERALADDITIONALMETHODSTOBEIMPLEMENTED4HISISACHIEVEDBEHINDTHESCENES by adding the ID to the URL first and then redirecting to the new URL. The changed URL is then passed to the browser. However you implement this, you must configure the new session ID manager in the web.config file: 8oaooekjOp]paik`a9EjLnk_ _kkgeahaoo9pnqa nacajan]paAtlena`OaooekjE`9b]hoa peiakqp9., oaooekjE@I]j]canPula9=lnaoo*OaooekjOp]pa*?k`a*IuOaooekjE@I]j]can(± =lnaoo*OaooekjOp]pa+: The oaooekjE@I]j]canPula attribute contains the class’s name and the namespace of the containing assembly. All other options depend on the entire configuration and are not directly associated with the session ID manager.
The Internal State Providers ASP.NET includes three internal state providers. You should investigate these before planning the development of a custom provider.
s EjLnk_OaooekjOp]paOpkna
s KqpKbLnk_OaooekjOp]paOpkna
s OmhOaooekjOp]paOpkna
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
This section gives a brief overview of the internal state providers, which have several features in common. You can control their behavior with the Aj]^haOaooekjOp]pa attribute located in the ]oa abstract CLASS SOYOUMUSTALSOIMPLEMENTTHEREQUIREDMEMBERSOFTHELnkre`an>]oa class. (Refer to Chapter 4 to read more about the structure of a provider.) The following tables list and describe the properties and methods you must implement from the abstract classes. The following example shows the basic implementation steps for a file-based store. This is not intended for real-life scenarios, but simply for learning and testing. Files are easy to monitor and you can see where the data goes (see Listing 6-7). Listing 6-7. A Simple File-Based Session State Persister qoejcOuopai7 qoejcOuopai*?khha_pekjo*Ola_e]heva`7 qoejcOuopai*EK7 qoejcOuopai*Sa^7 qoejcOuopai*Sa^*?kjbecqn]pekj7 qoejcOuopai*Sa^*OaooekjOp]pa7 j]iaol]_a=lnaoo*Atpajoe^ehepu*OaooekjOp]pa w lq^he_oa]ha`_h]ooBehaOaooekjOp]paOpkna6OaooekjOp]paOpknaLnkre`an>]oa w lner]paOaooekjOp]paOa_pekjoaoo_kjbec9jqhh7 lner]paopnejc^]oaL]pd7 lner]paopnejc]llhe_]pekjJ]ia7 lq^he_opnejc=llhe_]pekjJ]ia w cap w napqnj]llhe_]pekjJ]ia7 y y lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(J]iaR]hqa?khha_pekj_kjbec% w eb$_kjbec99jqhh% w pdnksjas=ncqiajpJqhhAt_alpekj$_kjbec%7 y eb$Opnejc*EoJqhhKnAilpu$j]ia%% w j]ia9BehaOaooekjOp]paOpkna7 y eb$Opnejc*EoJqhhKnAilpu$_kjbecW`ao_nelpekjY%% w _kjbec*Naikra$`ao_nelpekj%7 _kjbec*=``$`ao_nelpekj(BehaOaooekjOp]paOpkna± lnkre`anat]ilha%7 y ^]oa*Ejepe]heva$j]ia(_kjbec%7 ]llhe_]pekjJ]ia9Ouopai*Sa^*Dkopejc*DkopejcAjrenkjiajp± *=llhe_]pekjRenpq]hL]pd7
267
268
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
^]oaL]pd9_kjbecW^]oaL]pdY7 Ouopai*?kjbecqn]pekj*?kjbecqn]pekj_bc9Sa^?kjbecqn]pekjI]j]can± *KlajSa^?kjbecqn]pekj$=llhe_]pekjJ]ia%7 oaoo_kjbec9$OaooekjOp]paOa_pekj%_bc± *CapOa_pekj$ouopai*sa^+oaooekjOp]pa%7 y lq^he_kranne`arke`@eolkoa$% w y lq^he_kranne`a^kkhOapEpaiAtlena?]hh^]_g$± OaooekjOp]paEpaiAtlena?]hh^]_gatlena?]hh^]_g% w napqnjb]hoa7 y lq^he_kranne`arke`Oap=j`Naha]oaEpaiAt_hqoera$Dppl?kjpatp_kjpatp(± opnejce`(± OaooekjOp]paOpkna@]p]epai(± K^fa_phk_gE`(± ^kkhjasEpai% w ++Oane]hevapdaOaooekjOp]paEpai?khha_pekj]o]opnejc* opnejcoaooEpaio9Oane]heva$$OaooekjOp]paEpai?khha_pekj%epai*Epaio%7 opnejcl]pd9L]pd*?ki^eja$^]oaL]pd(Opnejc*Bkni]p$w,y*oo`(e`%%7 BehaOpna]ibo9jqhh7 pnu w eb$jasEpai% w eb$Beha*Ateopo$l]pd%% w Beha*@ahapa$l]pd%7 y bo9jasBehaOpna]i$l]pd(BehaIk`a*?na]paJas(± Beha=__aoo*Snepa(BehaOd]na*Na]`%7 y ahoa w ++ql`]paepai bo9jasBehaOpna]i$l]pd(BehaIk`a*Klaj(± Beha=__aoo*Snepa(BehaOd]na*Na]`%7 y Opna]iSnepanos9jasOpna]iSnepan$bo%7 os*Snepa$oaooEpaio%7 os*?hkoa$%7 y _]p_d$EKAt_alpekjat_alpekj% w ++]``annknd]j`hejc y bej]hhu w eb$bo9jqhh% w bo*?hkoa$%7
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
bo*@eolkoa$%7 y y y lq^he_kranne`aOaooekjOp]paOpkna@]p]CapEpai$Dppl?kjpatp_kjpatp(± opnejce`(± kqp^kkhhk_ga`(± kqpPeiaOl]jhk_g=ca( kqpk^fa_phk_gE`(± kqpOaooekjOp]pa=_pekjo]_pekjBh]co% w napqnjCapOaooekjOpknaEpai$b]hoa(_kjpatp(e`(kqphk_ga`(± kqphk_g=ca(kqphk_gE`(kqp]_pekjBh]co%7 y lq^he_kranne`aOaooekjOp]paOpkna@]p]CapEpaiAt_hqoera$Dppl?kjpatp_kjpatp(± opnejce`(± kqp^kkhhk_ga`(± kqpPeiaOl]jhk_g=ca( kqpk^fa_phk_gE`(± kqpOaooekjOp]pa=_pekjo]_pekjBh]co% w napqnjCapOaooekjOpknaEpai$pnqa(_kjpatp(e`(kqphk_ga`(± kqphk_g=ca(kqphk_gE`(kqp]_pekjBh]co%7 y lner]paOaooekjOp]paOpkna@]p]CapOaooekjOpknaEpai$^kkhhk_gNa_kn`( Dppl?kjpatp_kjpatp(± opnejce`(± kqp^kkhhk_ga`(± kqpPeiaOl]jhk_g=ca( kqpk^fa_phk_gE`(± kqpOaooekjOp]pa=_pekjo]_pekjBh]co% w ++Ejepe]hr]hqaobknNapqnjr]hqa]j`kqpl]n]iapano* OaooekjOp]paOpkna@]p]epai9jqhh7 hk_g=ca9PeiaOl]j*Vank7 hk_gE`9jqhh7 hk_ga`9b]hoa7 ]_pekjBh]co9OaooekjOp]pa=_pekjo*Jkja7 opnejcoane]heva`Epaio9Opnejc*Ailpu7 opnejcl]pd9L]pd*?ki^eja$^]oaL]pd(Opnejc*Bkni]p$w,y*oo`(e`%%7 BehaOpna]ibo9jqhh7 pnu w bo9jasBehaOpna]i$l]pd(BehaIk`a*Klaj(Beha=__aoo*Na]`(± BehaOd]na*Na]`%7 Opna]iNa]`anon9jasOpna]iNa]`an$bo%7 oane]heva`Epaio9on*Na]`PkAj`$%7 on*?hkoa$%7 y _]p_d$EKAt_alpekj% w ++]``annknd]j`hejcdana y bej]hhu
269
270
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
w eb$bo9jqhh% w bo*?hkoa$%7 bo*@eolkoa$%7 y y epai9@aoane]heva$_kjpatp(oane]heva`Epaio(-,.0%7 napqnjepai7 y lner]paop]pe_opnejcOane]heva$OaooekjOp]paEpai?khha_pekjepaio% w IaiknuOpna]iio9jasIaiknuOpna]i$%7 >ej]nuSnepansnepan9jas>ej]nuSnepan$io%7 eb$epaio9jqhh% w epaio*Oane]heva$snepan%7 y snepan*?hkoa$%7 napqnj?kjranp*Pk>]oa20Opnejc$io*Pk=nn]u$%%7 y lner]paop]pe_OaooekjOp]paOpkna@]p]@aoane]heva$Dppl?kjpatp_kjpatp(± opnejcoane]heva`Epaio(± ejppeiakqp% w IaiknuOpna]iio9jasIaiknuOpna]i$± ?kjranp*Bnki>]oa20Opnejc$oane]heva`Epaio%%7 OaooekjOp]paEpai?khha_pekjoaooekjEpaio9± jasOaooekjOp]paEpai?khha_pekj$%7 eb$io*Hajcpd:,% w >ej]nuNa]`anna]`an9jas>ej]nuNa]`an$io%7 oaooekjEpaio9OaooekjOp]paEpai?khha_pekj*@aoane]heva$na]`an%7 y napqnjjasOaooekjOp]paOpkna@]p]$oaooekjEpaio(± OaooekjOp]paQpehepu*CapOaooekjOp]pe_K^fa_po$_kjpatp%(peiakqp%7 y lq^he_kranne`arke`Naha]oaEpaiAt_hqoera$Dppl?kjpatp_kjpatp(± opnejce`(k^fa_phk_gE`% w ++naha]oahk_g y
lq^he_kranne`arke`NaikraEpai$Dppl?kjpatp_kjpatp(opnejce`(± k^fa_phk_gE`(OaooekjOp]paOpkna@]p]epai% w opnejcl]pd9L]pd*?ki^eja$^]oaL]pd(Opnejc*Bkni]p$w,y*oo`(e`%%7 Beha*@ahapa$l]pd%7 y lq^he_kranne`arke`?na]paQjejepe]heva`Epai$Dppl?kjpatp_kjpatp(opnejce`( ejppeiakqp% w
C H A P T E R 6 N P A G E A N D S E S S I O N M A N A G E M E N T
opnejcl]pd9L]pd*?ki^eja$^]oaL]pd(Opnejc*Bkni]p$w,y*oo`(e`%%7 BehaOpna]ibo9Beha*?na]pa$l]pd%7 bo*?hkoa$%7 y lq^he_kranne`aOaooekjOp]paOpkna@]p]?na]paJasOpkna@]p]$ Dppl?kjpatp_kjpatp(ejppeiakqp% w napqnjjasOaooekjOp]paOpkna@]p]$jasOaooekjOp]paEpai?khha_pekj$%(± OaooekjOp]paQpehepu*CapOaooekjOp]pe_K^fa_po$_kjpatp%(peiakqp%7 y lq^he_kranne`arke`NaoapEpaiPeiakqp$Dppl?kjpatp_kjpatp(opnejce`% w ++nabnaodepai y lq^he_kranne`arke`Ejepe]hevaNamqaop$Dppl?kjpatp_kjpatp% w y lq^he_kranne`arke`Aj`Namqaop$Dppl?kjpatp_kjpatp% w y y y The implementation lacks error handling and several advanced features. However, the basic parts of the session provider are implemented to demonstrate how you can implement actions appropriate to the provider. Essentially, the provider obtains session data, serializes it, and stores it in a file. The expiration handling is based on the file’s last access timestamp. Concurrent access is also handled on the file level.
Configuring the Provider Before you start working with the provider, you must configure the oaooekjOp]pa element in the web.config file, as shown in Listing 6-8. Listing 6-8. Configuring the Provider 8oaooekjOp]pa _kkgeahaoo9pnqa nacajan]paAtlena`OaooekjE`9b]hoa peiakqp9., ik`a9?qopki _qopkiLnkre`an9BehaOaooekjLnkre`an oaooekjE@I]j]canPula9=lnaoo*OaooekjOp]pa*?k`a*OeilhaOaooekjE@I]j]can( =lnaoo*OaooekjOp]pa: 8lnkre`ano: 8]``j]ia9BehaOaooekjLnkre`an pula9=lnaoo*Atpajoe^ehepu*OaooekjOp]pa*BehaOaooekjOp]paOpkna( =lnaoo*Atpajoe^ehepu*OaooekjOp]pa
271
272
C H APT ER 6 N PA G E A ND S ES S ION MA NA G EMENT
^]oaL]pd9_6Xpail+: 8+lnkre`ano: 8+oaooekjOp]pa: 4HEREAREATLEASTTHREEREQUIREDSETTINGS&IRSTLY THEik`a must be set to “Custom”. Secondly, the _qopkiLnkre`an attribute must be set to the name of the provider. Finally, the provider itself must be configured as a sub-element of the oaooekjOp]pa element. The additional ^]oaL]pd attribute is a private configuration setting used in the previous code. With these settings, all session data is written to and retrieved from the file system, using the path C:\temp for the default (root) application and c:\ temp\ for any other application.
Summary )NTHISCHAPTER WEEXAMINEDSEVERALPERSISTENCETECHNIQUES ANDDISCOVEREDTHATBOTHPAGEPERSISTers and session state persisters exist in order to overcome the limitations of HTTP’s statelessness. Page persisters store page or form related data using control state and view state. The default storage location is a hidden field within the form. An alternative method is to store the data in local memory. The provider model is used to customize this storage, and the sample implementation demonstrates how to store data in the file system. Session state persisters, which are also based on the provider model, store session-related data. The preceding example also shows how to replace the default storage with custom storage, and how to store the data in the file system. Extending persisters is a great way of improving performance, maintaining data within a web garden or farm, adding special features in order to handle specific environmental conditions, or better serving particular clients, such as mobile devices.
C HAPTER
7
Security and User Management
M
ost business applications include user management. Such applications must therefore handle security and user settings. In ASP.NET, you’ll find a comprehensive collection of features, services, classes, and controls to manage users, as well as their roles, settings, and access conditions. In this chapter, you’ll learn about
s 4HECOMPONENTSFORMINGSECURITYANDUSERMANAGEMENT
s %XTENDINGTHEUNDERLYINGPROVIDERS ESPECIALLYTHE-EMBERSHIPAND2OLEPROVIDERS
s #USTOMIZINGANDEXTENDINGTHE0ROFILEPROVIDER
!LLOFTHESECAPABILITIESAREBASEDONTHEPROVIDERMODELINTRODUCEDIN#HAPTER/FCOURSE THATISTHERESOURCEFORMOREABOUTTHEBASICDESIGNOFPROVIDERSANDHOWTOEXTENDANDCONFIGURE them.)
Built-In Capabilities #ONCEPTUALLY !30.%4EMPLOYSTHEPRINCIPLEOFhGATEKEEPERSv!GATEKEEPERISAMODULETHATSITS ONTOPOFTHEPIPELINEOBSERVINGEVERYINCOMINGREQUEST,IKEANYOTHERMODULE ITSIMPLEMENTATION is based on the EDpplIk`qha interface. There are usually several such modules in a row, each of them HANDLINGASPECIFICKINDOFACCESSSECURITYORAUTHORIZATION!SYOUVELEARNEDIN#HAPTERSTO THE PROVIDERANDMODULEMODELSAREHIGHLYEXTENSIBLE4HISGIVESYOUTHEOPPORTUNITYTOADDNEWMODules specific to the needs of your application and your desired security levels. /NEOFTHEBASICCONCEPTSOFWEBSECURITYISTHESTATELESSNESSOF(440"ECAUSETHEPROTOCOL ESSENTIALLYUSESAhFIREANDFORGETvAPPROACH THESECURITYASPECTSOFEVERYREQUESTMUSTBECHECKED 3INCESUBSEQUENTACTIONSMIGHTREQUIREAUTHORIZATIONORAUTHENTICATION THESECURITYMODULESARE POSITIONEDFIRSTINTHELINEINVOKEDBYPIPELINEEVENTS DEPENDINGONACTUALCONFIGURATIONASSEENIN Figure 7-1. The pipeline fires events in a specific order. First, the user is authenticated via the =qpdajpe_]paNamqaop event!FTERESTABLISHINGTHEUSERSIDENTITY AUTHORIZATIONOCCURS AND the =qpdknevaNamqaop event allows the user access to certain resources. The order in which these events are fired is crucial, as the session state becomes available only after both events are handled SEE&IGURE 4HISMEANSTHATNOSESSION RELATEDDATACANBESTOREDTOSUPPORTTHEAUTHENTICATIONANDAUTHORIZATIONMODULES
273
274
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Figure 7-1. The chain of authentication and authorization providers After successful user identification, their credentials are stored as an object containing their information. These credentials consist of a username/password pair and, optionally, additional data SUCHASROLESORLIFETIME$IFFERENTAUTHORIZATIONTECHNIQUES, such as Windows authentication or "ASICAUTHENTICATION CREATEDIFFERENTSETSOFCREDENTIALS)NFORMATIONTHATMUSTBEAVAILABLEDURING THEWHOLEPROCESSINGCYCLEISBOUNDTOTHECURRENTCONTEXT NAMELYTHEDppl?kjpatp object. The user credentials are stored in the Qoan property, and can be accessed in this manner: Ouopai*Sa^*Oa_qnepu*ELnej_el]hqoan9?kjpatp*Qoan7 ?kjpatp is provided by the L]ca class. If you’re not using the L]ca class, Dppl?kjpatp*?qnnajp* Qoan retrieves the same object. The ELnej_el]h interface is a simple definition for a credential store. It contains the user idenTITYACCESSIBLETHROUGHTHEE`ajpepu property provided via an object that implements EE`ajpepu) and the EoEjNkha method TOPROVIDEBASICAUTHORIZATIONSUPPORT(OWEVER THISISNTSUFFICIENT for typical applications. Several different implementations of these interfaces are available that fill in the gaps. EE`ajpepu provides additional support properties, while =qpdajpe_]pekjPula indiCATESWHICHMETHODWASUSEDTOAUTHENTICATEAUSERSUCHASh&ORMS vh.4,- vORh#USTOMv Eo=qpdajpe_]pa` reveals that one module of the chain of authentication modules has identified THEUSER%ACHMODULECANCHECKWHETHERTHEPREVIOUSMODULEHASIDENTIFIEDTHEUSERAND IFSO
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
CREATETHEAPPROPRIATEIDENTITYOBJECTANDSKIPTHEMODULESOWNAUTHENTICATIONPROCESS&INALLY the username is stored in the J]ia property of the EE`ajpepuOBJECTASAUNIQUEKEY4HEREARESEVeral built-in implementations of the EE`ajpepu interface:
s Ouopai*Sa^*Oa_qnepu*BknioE`ajpepu
s Ouopai*Oa_qnepu*Lnej_el]h*Sej`ksoE`ajpepu
s Ouopai*Oa_qnepu*Lnej_el]h*Cajane_E`ajpepu
The namespaces reveal that the identity model is not limited to Web applications. Instead, the IDENTITYMODELISEMBEDDEDWITHINTHEFRAMEWORKSSECURITYCOMPONENTS ANDASWITHMOSTTHINGS in .NET, itISEXTENSIBLE
Figure 7-2. Application events involved in the authentication and authorization cycle
275
276
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Authentication Modules Several authentication modulesAREAVAILABLEIN!30.%4"EFOREBUILDINGYOUROWNMODULE CONSIDERMODIFYINGANEXISTINGONE)DENTIFYINGAMODULECLOSETOYOURREQUIREMENTSANDCUSTOMIZINGIT is always a simpler solution than writing a new module from scratch. ASP.NET provides these core modules:
s &ORMS!UTHENTICATION-ODULE
s 7INDOWS!UTHENTICATION-ODULE
4HE&ORMS!UTHENTICATION-ODULE provides a simple form-based technique which is highly COMPATIBLEWITHMOSTEXISTINGCLIENTS BUTITISNOTTHEMOSTSECURE5NLESSTHECONNECTIONBETWEEN THECLIENTANDSERVERUSES33,SECURESOCKETLAYER THEUSERNAMEANDPASSWORDTRAVELUNENCRYPTED at least once through the Internet. For most applications, this isn’t acceptable. The WindowsAuTHENTICATION-ODULE, on the other hand, requires specific support from the client. Although most BROWSERSHANDLEITWELL KEEPINMINDTHATSOMEDEVICESSUCHASMOBILEGADGETS COULDFAIL4HE 0ASSPORT!UTHENTICATION-ODULEISNOTLISTEDBECAUSEITISNTAVIABLEALTERNATIVEANDMARKEDAS DEPRECATED-ICROSOFTS0ASSPORTCONCEPTWASDISCONTINUEDANDHASNOWBEENREPLACEDBY,IVE)$ which has not yet attracted wide-spread client support.
Set Up the Authentication Module 4OSETUPANAUTHENTICATIONMODULE YOULLNEEDTOMAKEANENTRYINTHEweb.config file. The element 8]qpdajpe_]pekj: is responsible for defining and configuring the appropriate module. As a module is responsible for handling all incoming requests, you can’t define the element in subfolders, and OVERRIDINGTHEROOTSETTINGSWOULDCREATEACONFLICTWITHINTHEPIPELINE(OWEVER THISWILLBEPOSSIBLEFORTHEAUTHORIZATIONMETHODDISCUSSEDINTHESECTIONh!UTHORIZATION-ODULESv The ik`a attribute has three commonly used values: 8]qpdajpe_]pekjik`a9Bknio+: 8]qpdajpe_]pekjik`a9Sej`kso+: 8]qpdajpe_]pekjik`a9Jkja+: !STHEREISNOh#USTOMvVALUE YOULLHAVETOUTILIZEONEOFTHEEXISTINGTECHNIQUESWHENYOU ADDYOUROWNMEMBERSHIPSYSTEM4HEh#USTOMvVALUEISNTPROVIDEDBECAUSEYOUDNEEDTO SUPPORTEXISTINGCLIENTS ANDWRITINGCUSTOMCLIENTSISNTFEASIBLE 7EBAPPLICATIONSTHATDEALWITH private data are usually protected by a form of transport layer security that allows the usage of forms authentication for publicly accessible sites and Windows authentication for intranet applications. For the configuration of forms authentication, the child element 8bkni: provides the appropriate settings.
NNote
IIS7 has several more capabilities to set up both authentication and authorization. Because it’s out of scope of this chapter, the security settings provided here are just to get the examples running. The description is in no way complete enough for security-driven applications.
Authorization Modules After authenticating the user, you need to determine what he or she is permitted to request. This is ALLABOUTACCESSINGRESOURCES!STHEREARETWOKINDSOFRESOURCESONA7EB3ERVER FILESAND52,S THEREARETWOBUILT INAUTHORIZATIONMODULES
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
s &ILE!UTHORIZATION
s 5RL!UTHORIZATION
)FYOUHAVEALREADYSETUPTHEMEMBERSHIPSYSTEMANDNOTDEFINEDTHEAUTHORIZATIONEXPLICITLY YOUMIGHTWONDERHOWITWORKSASEXPECTED4HISISBECAUSE!30.%4USESTHEBeha=qpdknev]pekj module if Windows authentication is set. No additional configuration step is required. Thus, if 7INDOWAUTHENTICATIONISUSED THE.4&3FILEACCESSSECURITYISEMPLOYED BASEDON7INDOWS!#,S ACCESSCONTROLLISTS
Using Impersonation )NTHECONTEXTOF Windows authentication, impersonation occurs when the identity of the logged-in user is changed and the new identity’s credentials are used to access resources. This is a common technique, but it’s dangerous. If the user base is large, and the roles and access policies grow, manAGEMENTBECOMESARDUOUS&URTHERMORE AUTHENTICATIONMISTAKESCREATESECURITYHOLES ASPARTSOF the application could run under an account other than that intended by the developer.
Set Up Authorization 4HE52,AUTHORIZATION controls access to directories and files. It can be configured by setting the appropriate child elements of the current 8]qpdknev]pekj:ELEMENT2ESTRICTIONSMAYBESETBASED ONBOTHUSERANDROLE&ILEAUTHORIZATIONDOESNOTREQUIREANYSETTINGSHERE 4OMANAGETHEAUTHORIZATION TWOELEMENTSARECRUCIAL8`aju: and 8]hhks:"OTHACCEPTAFEW attributes:
s 5SERS: A list of user accounts
s 2OLES: A list of roles
s 6ERBS!LISTOF(440TRANSMISSIONMETHODS
5SINGTHESESETTINGS YOUCANEXPLICITLYALLOWORDENYACCESSTOUSERS ROLES ORREGARDINGA(440 METHODLIKE'%4OR0/344HEqoano attribute accepts two special characters. You can specify all USERSWITHANASTERISKh vANDALLANONYMOUSUSERSWITHAQUESTIONMARKhv,ISTSOFBOTHROLESAND users are comma-separated.
The User Management Interfaces Included with ASP.NET are a number of tools to support user management, the details of which are BEYONDTHESCOPEOFTHISBOOK(OWEVER )LLGIVEAGENERALOVERVIEWOFTHEEXTENSIBILITYINTERFACES There are three parts responsible for handling user management:
s -EMBERSHIPSERVICE
s 2OLESERVICE
s 0ROFILESERVICE
Membership is about user identification and management. With the appropriate classes and controls, you can create, edit, and delete user accounts. This includes login and logout facilities BASEDONCONTROLS ASWELLASSEVERALFEATURESTHATENHANCEUSEREXPERIENCESUCHASPASSWORD recovery or ability to view the current name and login status). ASP.NET includes several built-in management tools to assist administrators with handling users, based on a SQL Server database store.
277
278
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
RolePERFORMSAUTHORIZATIONTASKS2OLESPROVIDEACCESSTORESOURCESANDCANBEASSIGNEDTO many users. If the user becomes a member of a role, he or she gains access to the resources the role is permitted to retrieve. The built-in management tools support the role model. Profile complements the user management by adding a user-specific data store. This allows you to maintain per-user settings between visits. Profiles are not intended to support session-related PROCESSES)NSTEAD THEYALLOWAUSERTOCUSTOMIZEASITESPECIFICALLYFORTHEIROWNPREFERENCESTHINK OFAh-Y3ITEvTYPEOFFUNCTIONALITY!30.%4INCLUDESMANYFEATURES SERVICES ANDOBJECTTYPES to support profiles.
Extensibility Issues 4HEEXTENSIBILITYMODEL is based on providers. The three services previously mentioned use proVIDERSTOSAVEANDRETRIEVETHEIRDATA"YDEFAULT DATAISSTOREDINA31,3ERVERDATABASE)TSOFTEN NECESSARYTOUSEALTERNATIVEOREXISTINGSTORAGE !CTIVE$IRECTORY ,$!0 ORACUSTOMSOLUTION 3EVERALBUILT INPROVIDERSAREINCLUDEDWITHEACHSERVICEINORDERTOSUPPORTDIFFERENTSTORESSEE &IGURE
Figure 7-3. The class hierarchy for built-in Membership and Role providers The default providers use a SQL Server database. These are the OmhIai^anodelLnkre`an and the OmhNkhaLnkre`an!30.%4ALSOSUPPORTS!CTIVE$IRECTORY!$ OUTOFTHEBOX VIATHE =_pera@ena_pknuIai^anodelLnkre`an. 4ABLE SHOWSALLTHEBASICMEMBERSOFTHE-EMBERSHIPPROVIDERCLASSES
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Table 7-1. Members of the MembershipProvider Classes*
Name
Description
Methods ?d]jcaL]ooskn`
#HANGESTHEPASSWORD
?d]jcaL]ooskn`Mqaopekj=j`=josan
#HANGESTHEPASSWORDQUESTIONANDANSWER
?na]paQoan
#REATESANEWUSER
@a_nulpL]ooskn`
Decrypts the password.
@ahapaQoan
Deletes a user.
Aj_nulpL]ooskn`
Encrypts the password.
Bej`Qoano>uAi]eh
Finds a user using his or her email address.
Bej`Qoano>uJ]ia
Finds a user using his or her name.
Cap=hhQoano
2ETRIEVESALISTOFALLUSERS
CapJqi^anKbQoanoKjheja
2ETRIEVESALISTOFALLCURRENTLYLOGGED INUSERS4HEBEhavior depends on the provider and does not necessarILYREPORTTHEEXACTVALUE
CapL]ooskn`
2ETRIEVESTHEPASSWORDFORAGIVENNAME
CapQoan
2ETRIEVESAOuopai*Sa^*Oa_qnepu*Iai^anodelQoan object based on the name.
CapQoanJ]ia>uAi]eh
2ETRIEVESTHENAMEUSINGANEMAILADDRESS
Ejepe]heva
)NITIALIZESTHEPROVIDER)NHERITEDFROMLnkre`an>]oa.
NaoapL]ooskn`
2ESETSTHECURRENTPASSWORDWITHANAUTOMATICALLY generated one.
Qjhk_gQoan
5NLOCKSALOCKEDUSER
Ql`]paQoan
5PDATESTHEUSERSINFORMATIONINTHEDATASTORE
R]he`]paQoan
#HECKSWHETHERTHEUSERWITHTHEPROVIDEDPASSWORDIS a legitimate user.
Events R]he`]pejcL]ooskn`
An event fired while the provider validates the password.
Properties =llhe_]pekjJ]ia
4HENAMEOFTHEAPPLICATION!-EMBERSHIPPROVIDER can support several applications and supports different data storage for each application. Inherited from Lnkre`an>]oa.
Aj]^haL]ooskn`Naoap
Defines whether or not the user can reset their password.
Aj]^haL]ooskn`Napnear]h
Defines whether or not the user can retrieve their password.
I]tEjr]he`L]ooskn`=ppailpo
$ETERMINESTHEMAXIMUMNUMBEROFFALSEPASSWORD ATTEMPTSALLOWEDBEFORETHEACCOUNTISLOCKED
IejNamqena`Jkj=hld]jqiane_?d]n]_pano
-INIMUMNUMBEROFNON ALPHANUMERICCHARACTERS a password must have.
IejNamqena`L]ooskn`Hajcpd
-INIMUMPASSWORDLENGTH
J]ia
Name of the provider. Inherited from Lnkre`an>]oa. Continued
279
280
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Table 7-1. Continued
Name
Description
L]ooskn`=ppailpSej`ks
0REVENTShBRUTEFORCEvATTEMPTSTOGUESSAPASSWORD If the I]tEjr]he`L]ooskn`=ppailpo occur within L]ooskn`=ppailpSej`ksINMINUTES THATUSERS ACCOUNTISLOCKED
L]ooskn`Bkni]p
Format in which the password is saved in the storage. This is a value from the enumeration Iai^anodelL]ooskn`Bkni]p, which provides the values ?ha]n, D]oda`, or Aj_nulpa`(ASHESAREUNIDIRECTIONAL ENCRYPTIONMETHODS4HEDEFAULTHASHFORMATIS3(!
L]ooskn`OpnajcpdNacqh]nAtlnaooekj
!REGULAREXPRESSIONTHATISUSEDTOCHECKWHETHER or not the password meets the minimum password strength required by your application.
NamqenaoMqaopekj=j`=josan
Determines whether the provider supports the password retrieval using the question and answer technique.
NamqenaoQjemqaAi]eh
Determines whether or not the email address must be unique in the database.
*Covers both SqlMembershipProvider and ActiveDirectoryMembershipProvider
&ORTHE2OLEPROVIDER THINGSLOOKALITTLEDIFFERENT4HE=qpdknev]pekjOpknaNkhaLnkre`an class SUPPORTSTHREEKINDSOFROLEDATASTORAGETHE!CTIVE$IRECTORYITSELF AN!CTIVE$IRECTORY!PPLICATION -ODESERVER!$!- ORAN8-,FILE&ORTHESETHREEMODES ACONNECTIONSTRINGDEFINESTHEDATA source. The connection string begins with iotih6++FORAN8-,FILE ORWITHioh`]l6++ for Active $IRECTORYOR!$!- ASSHOWNINTHEFOLLOWINGCODE 8_kjja_pekjOpnejco: 8]``j]ia9=qpdknev]pekjOanre_ao± _kjja_pekjOpnejc9iotih6++zX=ll[@]p]XIu=`]iOpkna*tih+: 8+_kjja_pekjOpnejco: The Sej`ksoPkgajNkhaLnkre`an uses the local Windows user database to retrieve the role information. Don’t confuse this with Windows groups. Direct access to Windows group information is not supported by this provider. While you can obtain membership information about a group, you cannot write, delete, or change these memberships even if management tools support this. From the perspective of ASP.NET, the Sej`ksoPkgajNkhaLnkre`an provides read-only access. None of these classes are sealed. This means that you can inherit from each class and override methods and properties to change behavior as desired. 4ABLE SHOWSALLBASICMEMBERSOFTHE2OLEPROVIDERCLASSES Table 7-2. Members of the RoleProvider Classes
Name
Description
Not Supported In*
=``QoanoPkNkhao
Add users to roles. Defined in NkhaLnkre`an.
W
?na]paNkha
#REATEANEWROLE$EFINEDINNkhaLnkre`an.
W
@ahapaNkha
Delete a role. Defined in NkhaLnkre`an.
W
Bej`QoanoEjNkha
Find all users within a role. Defined in NkhaLnkre`an.
W, A
Cap=hhNkhao
'ETALISTOFALLROLES$EFINEDINNkhaLnkre`an.
W
Methods
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Name
Description
CapNkhaoBknQoan
'ETALLROLESFORAUSER$EFINEDINNkhaLnkre`an.
CapQoanoEjNkha
'ETALLUSERSINAROLE$EFINEDINNkhaLnkre`an.
EoQoanEjNkha
Indicates whether or not a user is in a role. Defined in NkhaLnkre`an.
NaikraQoanoBnkiNkhao
2EMOVEUSERSFROMROLES$EFINEDIN NkhaLnkre`an.
NkhaAteopo
)NDICATESWHETHERTHEROLEEXISTS$EFINEDIN NkhaLnkre`an.
Not Supported In* W
W
Properties =llhe_]pekjJ]ia
.AMEOFTHEAPPLICATION!2OLEPROVIDERCAN support several applications and different data storage for each application. Defined in Lnkre`an>]oa.
@ao_nelpekj
Description used in Lnkre`an>]oa.
O_klaJ]ia
3COPEFORTHEAUTHORIZATIONSTORE
W, S
?]_daNabnaodEjpanr]h
4IMEINMINUTES THEPROVIDERCACHESROLE information.
W, S
*W = WindowsTokenRoleProvider, S = SqlRoleProvider, A = AuthorizationStoreRoleProvider
With this overview of the capabilities of different providers, you can now investigate how to EXTENDTHEM
Extending Membership and Role Providers The functionality ofTHEBUILT INPROVIDERSSUITSMOSTCOMMONTYPESOFAPPLICATIONS"EFORECONSTRUCTINGYOUROWNPROVIDER LETSTAKEALOOKATWHENANDWHYITSMOREPRACTICALTOCREATEOREXTEND a built-in one.
Why Create a Membership Provider? 4HEPURPOSEOFAPROVIDERISTOMANAGEACCESSTODATASTORAGE%XTENDINGTHEPROVIDERMODELIS necessary if you want to use a different storage for your user database. The OmhIai^anodelLnkre`an supports a SQL Server database, while the =_pera@ena_pknuIai^anodelLnkre`an supports the Active $IRECTORY#REATINGASERVICEINFRASTRUCTUREFOLLOWINGTHEPRINCIPLESOFSERVICE ORIENTEDARCHITECTURES 3/! REQUIRESANOTHERMETHOD7EB3ERVICES/NEREASONFORCREATINGACUSTOM-EMBERSHIP provider is the ability to use Web Services. As shown in Table 7-1, the Iai^anodelLnkre`an base class supports an incredible number of features. Whether you require some or all of these depends on the needs of your application. )NTHEFOLLOWINGEXAMPLE )LLDEMONSTRATETHEBASICPRINCIPLESOFACUSTOM-EMBERSHIPPROvider in the particular case of authenticating users against a Web Service. The service provides a transparent tier against a data store. This means that the service can run anywhere in your organiZATIONASACENTRALPOINTOFSERVICE ANDTHEUSERSOFTHESERVICETHEPROVIDERKNOWSNOTHINGABOUT the data storage behind the service. Security is enhanced by hiding all data storage implementation details behind the publicly visible Web Service methods. For simplicity and demonstration purposes ONLY THEEXAMPLESERVICESTORESUSERINFORMATIONINASIMPLE8-,FILE4HISALLOWSYOUTOOBSERVE THEFILEANDLEARNEXACTLYHOWTHEPROVIDERBEHAVES )NTHEFOLLOWINGSECTIONS )LLSHOWTHESAMPLECODEANDEXPLAINTHEIMPLEMENTATIONDETAILS
281
282
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Solution Details $EVELOPINGACUSTOMPROVIDERFIRSTREQUIRESADATASOURCE)NTHISEXAMPLE )LLUSEAN8-,FILEAS STORAGEANDA7#&7INDOWS#OMMUNICATION&OUNDATION) service to access the file remotely. This demonstrates that it’s possible to transparently access a remote data store using standard frameWORKTECHNIQUES The solution consists of the following:
s !7#&SERVICEPROJECTWITHTWOSERVICES ONEFORMEMBERSHIPANDTHEOTHERFORROLES
s !7EBPROJECTCONFIGUREDTOUSETHECUSTOMPROVIDERTHATHAS s !NIMPLEMENTATIONOFACUSTOM-EMBERSHIPPROVIDER s !NIMPLEMENTATIONOFACUSTOM2OLEPROVIDER s !SERVICEREFERENCETOUSETHE7#&SERVICE
Developing Membership and Role Providers "OTH-EMBERSHIPAND2OLEPROVIDERSSUPPORT several features. Whether or not you implement all FEATURES EXTENDADDITIONALONES ORSIMPLYCREATEARUDIMENTARYSERVICEDEPENDSONYOURAPPLICATIONANDREQUIREMENTS)NTHISEXAMPLE )LLIMPLEMENTALLTHEBASICFEATURESVIAASIMPLIFIED approach for theSAKEOFBREVITY
Create Web Service–Driven Membership Provider The Web Service–driven-EMBERSHIPPROVIDERCONSISTSOFTHEFOLLOWINGTHREEPARTS 1. 4HEUSERDATASTOREAN8-,FILE 2. The service tier—a Web Service with several useful methods 3. 4HE-EMBERSHIP provider implementation
Create Web Service–Driven Role Provider The Web service–driven2OLEPROVIDERALSO consists of the following three parts: 1. 4HESAME8-,FILEASUSEDINTHEUSERDATASTORE EXTENDEDWITHROLEPROPERTIES 2. The service tier—a Web Service with several useful methods 3. 4HE2OLEPROVIDERIMPLEMENTATION &ORBOTHPROVIDERS WELLNEEDASIMPLETESTENVIRONMENT#ONSIDERCREATINGSOMEPAGESUSING various Login controls, such as the ?na]paQoanSev]n`. This functions well but requires additional WORK4OBEGINWITH )RECOMMENDUSINGTHEBUILT IN!30.%4CONFIGURATIONAPPLICATION,AUNCHIT LOCALLYIN6ISUAL3TUDIOFROMTHEProject and ASP.NET configurationMENUITEMS5SINGTHEEMBEDDED7EB3ERVER 6ISUAL3TUDIOSTARTSA7EBAPPLICATIONTHATALLOWSYOUTOMANAGEUSERSANDROLES WITHTHECURRENTPROVIDER"EFOREDOINGSO YOULLNEEDTOCONFIGURETHEPROVIDERS(OWEVER WELL begin by developing the entire application.
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Developing the Service Tier The service tier uses7#&TOCREATETWO7EB3ERVICES ONEFORMEMBERSHIPMANAGEMENTANDONEFOR role management. To create the service: 1. 7ITHIN6ISUAL3TUDIO CREATEANEWPROJECTVIAFile ° New ° Project ° Web ° WCF Service Library. 2. .AMETHEPROJECTh-EMBERSHIPv 3. Add two services to the project using AddFROMTHECONTEXTMENU3ELECTNew Item, and in the following dialog choose WCF ServiceSEE&IGURE
Figure 7-4. Adding a new WCF service to the service library 4HEWIZARDCREATESASERVICEINTERFACETHECONTRACTFORTHESERVICE THECLASSWITHTHEIMPLEmentation, and the required entries in web.config or App.config file to define the service endpoint. The configuration file depends on type of project. The endpoint is a definition used later in the Web application where you can access the service. The contract is usually an interface decorated with the WOanre_a?kjpn]_pY attribute%ACHMETHODTHATISEXPOSEDASASERVICECALLISDECORATEDWITHTHE WKlan]pekj?kjpn]_pY attribute"OTHATTRIBUTESSUPPORTSEVERALSETTINGSFORMODIFYINGBEHAVIORAND APPEARANCE(OWEVER THISISBEYONDTHESCOPEOFTHISBOOK ANDDEFAULTSETTINGSCANBEUSEDFOR this EXAMPLEASWELL
NTip If you want to learn more about professional WCF development, refer to the book Pro WCF: Practical Microsoft SOA Implementation by Amit Bahree, Shawn Cicoria, Dennis Mulder, Nishith Pathak, and Chris Peiris (Apress, 2007).
283
284
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Creating the Membership Service 4HE-EMBERSHIPSERVICE consists of the following interfaces and classes:
s )-EMBERSHIP3ERVICEINTERFACE
s -EMBERSHIP3ERVICESERVICE
s -EMBERSHIP3ERVICECLASS
s 5SER$ATACLASS
s 5SERCLASS
s &ILE-ANAGERCLASS !LLTHECODETOBUILDTHESERVICEISSHOWNANDEXPLAINEDINTHEFOLLOWING
NNote
For sake of brevity and space, Listing 7-1 lacks error handling, “using” statements, and namespace definitions. Additionally, to get the example running, a reference to System.web.dll is required. Please refer to the sample code provided with the book for the full implementation.
Listing 7-1. The IMembershipService Interface Defines the Contract WOanre_a?kjpn]_pY lq^he_ejpanb]_aEIai^anodelOanre_a w WKlan]pekj?kjpn]_pY ^kkh?d]jcaL]ooskn`$opnejcqoanj]ia(opnejckh`L]ooskn`(opnejcjasL]ooskn`%7 WKlan]pekj?kjpn]_pY ^kkh?d]jcaL]ooskn`Mqaopekj=j`=josan$opnejcqoanj]ia(opnejcl]ooskn`(± opnejcjasL]ooskn`Mqaopekj(± opnejcjasL]ooskn`=josan%7 WKlan]pekj?kjpn]_pY Qoan?na]paQoan$opnejcqoanj]ia(opnejcl]ooskn`(opnejcai]eh(± opnejcl]ooskn`Mqaopekj(opnejcl]ooskn`=josan(± ^kkheo=llnkra`(k^fa_plnkre`anQoanGau(± kqpIai^anodel?na]paOp]pqoop]pqo%7 WKlan]pekj?kjpn]_pY ^kkh@ahapaQoan$opnejcqoanj]ia(^kkh`ahapa=hhNah]pa`@]p]%7 WKlan]pekj?kjpn]_pY Heop8Qoan:Bej`Qoano>uAi]eh$opnejcai]ehPkI]p_d(ejpl]caEj`at(± ejpl]caOeva(kqpejppkp]hNa_kn`o%7 WKlan]pekj?kjpn]_pY Heop8Qoan:Bej`Qoano>uJ]ia$opnejcqoanj]iaPkI]p_d(ejpl]caEj`at(± ejpl]caOeva(kqpejppkp]hNa_kn`o%7 WKlan]pekj?kjpn]_pY Heop8Qoan:Cap=hhQoano$ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o%7
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
WKlan]pekj?kjpn]_pY ejpCapJqi^anKbQoanoKjheja$%7 WKlan]pekj?kjpn]_pY opnejcCapL]ooskn`$opnejcqoanj]ia(opnejc]josan%7 WKlan]pekj?kjpn]_p$J]ia9CapQoan^uJ]ia%Y QoanCapQoan$opnejcqoanj]ia(^kkhqoanEoKjheja%7 WKlan]pekj?kjpn]_pY opnejcCapQoanJ]ia>uAi]eh$opnejcai]eh%7 WKlan]pekj?kjpn]_pY opnejcNaoapL]ooskn`$opnejcqoanj]ia(opnejc]josan%7 WKlan]pekj?kjpn]_pY ^kkhQjhk_gQoan$opnejcqoanJ]ia%7 WKlan]pekj?kjpn]_pY rke`Ql`]paQoan$Qoanqoan%7 WKlan]pekj?kjpn]_pY ^kkhR]he`]paQoan$opnejcqoanj]ia(opnejcl]ooskn`%7 y The interface is not unusual. Each method decorated with the Klan]pekj?kjpn]_p attribute is EXPOSEDBYTHESERVICE!SSHOWNINTHELISTINGFORTHECapQoan method, the method’s name can be MODIFIED4HISISNECESSARYIFYOUWISHTOUSEOVERLOADEDMETHODSMETHODSTHATHAVETHESAME NAME BUTADIFFERENTPARAMETERLISTSIGNATURE 4HESERVICELACKSTHISCAPABILITYANDREQUIRESUNIQUE names. The Klan]pekj?kjpn]_p attribute’s J]iaPROPERTYSEPARATESINTERNALLOGICFROMTHEEXTERNAL service façade. /NCETHEINTERFACEISCOMPLETED ITMUSTBEIMPLEMENTED"EGINWITHASERVICEFILETHATLINKSTO ACODE BEHINDFILESEE,ISTINGS Listing 7-2. The MembershipService.svc File Is Required in Order to Publish the Service 8!adej`9Iai^anodelOanre_a*or_*_o!: 4HELOGICOFTHESERVICEISCODEDINTHECODE BEHINDFILE SHOWNIN,ISTING Listing 7-3. The MembershipService Class Is the Implementation WBehaEKLanieooekj$Oa_qnepu=_pekj*Hejg@ai]j`%Y lq^he__h]ooIai^anodelOanre_a6EIai^anodelOanre_a w lq^he_^kkh?d]jcaL]ooskn`$opnejcqoanj]ia(opnejckh`L]ooskn`(± opnejcjasL]ooskn`% w pdnksjasJkpEilhaiajpa`At_alpekj$%7 y lq^he_^kkh?d]jcaL]ooskn`Mqaopekj=j`=josan$opnejcqoanj]ia(opnejcl]ooskn`(± opnejcjasL]ooskn`Mqaopekj(±
285
286
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
opnejcjasL]ooskn`=josan% w pdnksjasJkpEilhaiajpa`At_alpekj$%7 y lq^he_Qoan?na]paQoan$opnejcqoanj]ia(opnejcl]ooskn`(opnejcai]eh(± opnejcl]ooskn`Mqaopekj(opnejcl]ooskn`=josan(± ^kkheo=llnkra`(k^fa_plnkre`anQoanGau(± kqpOuopai*Sa^*Oa_qnepu*Iai^anodel?na]paOp]pqoop]pqo% w Qoanqoan9jqhh7 Qoan@]p]q`9BehaI]j]can*Hk]`$%7 ++_da_gqoano(_kjoe`an]``ejcikna`]p]dana r]nd]oQoan9bnkiqejq`*Qoanosdanaq*QoanJ]ia*Amq]ho$qoanj]ia%oaha_pq7 eb$d]oQoan*?kqjp$%:,% w op]pqo9Iai^anodel?na]paOp]pqo*@qlhe_]paQoanJ]ia7 napqnjjqhh7 y r]nd]oAi]eh9bnkiqejq`*Qoanosdanaq*Ai]eh*Amq]ho$ai]eh%oaha_pq7 eb$d]oAi]eh*?kqjp$%:,% w op]pqo9Iai^anodel?na]paOp]pqo*@qlhe_]paAi]eh7 napqnjjqhh7 y pnu w qoan9jasQoan$ qoanj]ia( ai]eh( l]ooskn`Mqaopekj( ( eo=llnkra`( b]hoa( @]paPeia*Jks( @]paPeia*IejR]hqa( @]paPeia*IejR]hqa( @]paPeia*Jks( @]paPeia*IejR]hqa%7 ++OpknaD]odKjhu qoan*L]ooskn`9BehaI]j]can*?]h_qh]paOD=-$l]ooskn`%7 q`*Qoano*=``$qoan%7 BehaI]j]can*O]ra$q`%7 op]pqo9Iai^anodel?na]paOp]pqo*Oq__aoo7 y _]p_d w op]pqo9Iai^anodel?na]paOp]pqo*Lnkre`anAnnkn7 y napqnjqoan7 y lq^he_^kkh@ahapaQoan$opnejcqoanj]ia(^kkh`ahapa=hhNah]pa`@]p]% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoan9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia%
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
oaha_pq%*BenopKn@ab]qhp8Qoan:$%7 eb$qoan9jqhh% w q`*Qoano*Naikra$qoan%7 BehaI]j]can*O]ra$q`%7 napqnjpnqa7 y napqnjb]hoa7 y lq^he_Heop8Qoan:Bej`Qoano>uAi]eh$opnejcai]ehPkI]p_d(ejpl]caEj`at(± ejpl]caOeva(kqpejppkp]hNa_kn`o% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoano9$bnkiqejq`*Qoano sdanaq*Ai]eh*Amq]ho$ai]ehPkI]p_d% oaha_pq%*PkHeop8Qoan:$%7 pkp]hNa_kn`o9qoano*?kqjp$%7 napqnjCapL]ca`$qoano(l]caEj`at(l]caOeva%7 y lq^he_Heop8Qoan:Bej`Qoano>uJ]ia$opnejcqoanj]iaPkI]p_d(ejpl]caEj`at(± ejpl]caOeva(kqpejppkp]hNa_kn`o% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoano9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]iaPkI]p_d% oaha_pq%*PkHeop8Qoan:$%7 pkp]hNa_kn`o9qoano*?kqjp$%7 napqnjCapL]ca`$qoano(l]caEj`at(l]caOeva%7 y lq^he_Heop8Qoan:Cap=hhQoano$ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 pkp]hNa_kn`o9q`*Qoano*?kqjp7 napqnjCapL]ca`$q`*Qoano(l]caEj`at(l]caOeva%7 y lner]paHeop8Qoan:CapL]ca`$Heop8Qoan:q`(ejpl]caEj`at(ejpl]caOeva% w l]caOeva9I]pd*Iej$q`*?kqjp(l]caOeva%7 napqnjq`*CapN]jca$l]caEj`at&l]caOeva(l]caOeva%7 y lq^he_ejpCapJqi^anKbQoanoKjheja$% w ++Qoanosdkhkcca`ejsepdejpdah]op-1iejo Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoano9$bnkiqejq`*Qoano sdanaq*H]op=_perepu@]pa*=``Iejqpao$-1%:@]paPeia*Jks oaha_pq%7 napqnjqoano*?kqjp$%7 y lq^he_opnejcCapL]ooskn`$opnejcqoanj]ia(opnejc]josan% w
287
288
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoan9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia%""± q*L]ooskn`=josan*Amq]ho$]josan% oaha_pq%*Benop8Qoan:$%7 eb$qoan9jqhh% w napqnjqoan*L]ooskn`7 y napqnjjqhh7 y lq^he_QoanCapQoan$opnejcqoanj]ia(^kkhqoanEoKjheja% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoan9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia%"" $qoanEoKjheja%; q*H]op=_perepu@]pa*=``Iejqpao$-1%:@]paPeia*Jks6 pnqa++]hhqoano oaha_pq%*BenopKn@ab]qhp8Qoan:$%7 napqnjqoan7 y lq^he_opnejcCapQoanJ]ia>uAi]eh$opnejcai]eh% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoan9$bnkiqejq`*Qoano sdanaq*Ai]eh*Amq]ho$ai]eh% oaha_pq%*BenopKn@ab]qhp8Qoan:$%7 eb$qoan9jqhh% w napqnjqoan*QoanJ]ia7 y napqnjjqhh7 y lq^he_opnejcNaoapL]ooskn`$opnejcqoanj]ia(opnejc]josan% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoan9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia%""± q*L]ooskn`=josan*Amq]ho$]josan% oaha_pq%*BenopKn@ab]qhp8Qoan:$%7 eb$qoan9jqhh% w napqnjqoan*L]ooskn`7 y napqnjjqhh7 y lq^he_^kkhQjhk_gQoan$opnejcqoanJ]ia% w pdnksjasJkpEilhaiajpa`At_alpekj$%7 y lq^he_rke`Ql`]paQoan$Qoanqoan%
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoanPkQl`]pa9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoan*QoanJ]ia% oaha_pq%*BenopKn@ab]qhp8Qoan:$%7 bkna]_d$LnklanpuEjbkleejqoan*CapPula$%*CapLnklanpeao$± >ej`ejcBh]co*Lq^he_x>ej`ejcBh]co*Ejop]j_a%% w LnklanpuEjbkleP]ncap9pulakb$Qoan%*CapLnklanpu$le*J]ia%7 eb$leP]ncap9jqhh% w leP]ncap*OapR]hqa$qoanPkQl`]pa(le*CapR]hqa$qoan(jqhh%(jqhh%7 y y BehaI]j]can*O]ra$q`%7 y lq^he_^kkhR]he`]paQoan$opnejcqoanj]ia(opnejcl]ooskn`% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 opnejcd]od9BehaI]j]can*?]h_qh]paOD=-$l]ooskn`%7 r]nqoan9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia%"" q*L]ooskn`*Amq]ho$d]od% oaha_pq%*BenopKn@ab]qhp8Qoan:$%7 qoan*H]op=_perepu@]pa9@]paPeia*Jks7 BehaI]j]can*O]ra$q`%7 napqnj$qoan9jqhh%7 y y -OSTOFTHESEMETHODSFOLLOWTHESAMECODEPATTERN4HECURRENTCONTENTOFTHEMEMBERSHIPDATASTOREAN8-,FILEISLOADEDBYCALLINGBehaI]j]can*Hk]`SEE,ISTING LATERINTHIS chapter). This ensures that changes made by parallel calls are handled properly. Each method uses a LINQ query to obtain the required data. If the method needs to persist changes, the updated Qoan@]p]OBJECTISSAVEDBACKTOTHESTORAGEFILE
NTip If you want to learn more about LINQ, I recommend LINQ for Visual C# 2008 by Fabio Claudio Ferracchiati (Apress, 2008). Passwords require special treatment. They aren’t usually stored in an unencrypted format, so you’ll need to choose to either encrypt the password or store a hash of the password. (ASHESAREAONE WAYENCRYPTIONTECHNIQUE4OVALIDATEAUSERLOGGINGIN YOUSIMPLYHASH their supplied password and compare the result with the stored hash value. Storing a hash is good PRACTICE BECAUSETHEHASHALGORITHMWASDESIGNEDTOMAKEITALMOSTIMPOSSIBLETODERIVETHE ORIGINALTEXTFROMTHEHASHVALUE/NTHEOTHERHAND THEHASHDOESNTSUPPORTPASSWORDRETRIEVAL Instead, the user receives a new password when he or she requests a lost one. This requires more effort, as the provider must create generic passwords, and the application must support password CHANGEFORMS ANDSOFORTH)NTHEEXAMPLE THEPASSWORDISSTOREDASAHASH DESPITETHEREBEINGNO PASSWORDGENERATOR4HEPASSWORDISHASHEDUSINGTHE3(!HASHALGORITHM4HISISSAFEANDEASY TOUSE,ISTING SHOWSTHEBehaI]j]can*?]h_qh]paOD=- method. If you plan to implement the provider in an application, consider using an encrypted password that you can decrypt on request.
289
290
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
4HECODEALSOHASAHARD CODEDSETTINGFORTHECALCULATIONOFTHENUMBEROFUSERSONLINETHE CapJqi^anKbQoanoKjheja method "ASEDONTHETIMEOFLASTACTIVITY ALLUSERSTHATHAVELOGGEDIN within the last 15 minutes are counted as being online. The H]op=_perepu@]pa property is set in the R]he`]paQoanMETHOD4HISISANEXTREMELYSIMPLIFIEDALGORITHM BUTITDEMONSTRATESONEPOSSIBLE approach. /NESIGNIFICANTMETHODIS?na]paQoan)TCHECKSSEVERALCONDITIONSINORDERTOAVOID DUPLICATEUSERNAMES EMAILADDRESSES OROTHERUNWANTEDDATA4HEEXAMPLECHECKSJUSTTWO of a wide range of possible conditions. First, it tests for duplicate user names. If this occurs, the method sets the property status to Iai^anodel?na]paOp]pqo*@qlhe_]paQoanJ]ia3ECOND ITCHECKS for duplicate email addresses and sets the status to Iai^anodel?na]paOp]pqo*@qlhe_]paAi]eh, if required. You can refer to the Iai^anodel?na]paOp]pqo enumeration to find more return values and CONDITIONSTOTEST)FEVERYTHINGCHECKSOUT THEIai^anodel?na]paOp]pqo*Oq__aoo value is set. The Iai^anodel?na]paOp]pqo*Lnkre`anAnnknCONDITIONISUSEDIFUNEXPECTEDERRORSARECAUGHTBYTHE pny)_]p_d statement. The class uses the Qoan@]p] classHEAVILY4HISCLASSISSTRAIGHTFORWARD CONTAININGASERIALIZABLE collection of Qoan and NkhaOBJECTS ASSHOWNIN,ISTING Listing 7-4. The UserData Class Is the Container for Serializing Users and Roles lq^he__h]ooQoan@]p] w lq^he_Qoan@]p]$% w y WTihAhaiajpY lq^he_Heop8Qoan:Qoano w cap7 oap7 y WTihAhaiajpY lq^he_Heop8opnejc:Nkhao w cap7 oap7 y y "ECAUSETHESERIALIZATIONUSESTHETihOane]hevan ALLELEMENTSREQUIRINGSERIALIZATIONARETAGGED with the TihAhaiajp attribute. The Qoan object contains all data pertaining to a specific account. This CLASSISSHOWNNEXTIN,ISTING Listing 7-5. The User Class Serializes a Single User WOane]hev]^haY W@]p]?kjpn]_pY lq^he__h]ooQoan w lq^he_Qoan$% w y
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
lq^he_Qoan$opnejcj]ia(opnejcai]eh(opnejcl]ooskn`Mqaopekj(± opnejc_kiiajp(^kkheo=llnkra`(^kkheoHk_ga`Kqp(± @]paPeia_na]pekj@]pa(@]paPeiah]opHkcej@]pa(± @]paPeiah]op=_perepu@]pa(@]paPeiah]opL]ooskn`?d]jca`@]pa(± @]paPeiah]opHk_gkqp@]pa% w pdeo*QoanJ]ia9j]ia7 pdeo*Ai]eh9ai]eh7 pdeo*?kiiajp9_kiiajp7 pdeo*Eo=llnkra`9eo=llnkra`7 pdeo*EoHk_ga`Kqp9eoHk_ga`Kqp7 pdeo*?na]pekj@]pa9_na]pekj@]pa7 pdeo*H]opHkcej@]pa9h]opHkcej@]pa7 pdeo*L]ooskn`Mqaopekj9l]ooskn`Mqaopekj7 pdeo*H]op=_perepu@]pa9h]op=_perepu@]pa7 pdeo*H]opL]ooskn`?d]jca`@]pa9h]opL]ooskn`?d]jca`@]pa7 pdeo*H]opHk_gkqp@]pa9h]opHk_gkqp@]pa7 y W@]p]Iai^anY WTihAhaiajpY lq^he_opnejc?kiiajpwcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_@]paPeia?na]pekj@]pawcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_opnejcAi]ehwcap7oap7y W@]p]Iai^anY WTih=ppne^qpaY lq^he_^kkhEo=llnkra`wcap7oap7y W@]p]Iai^anY WTih=ppne^qpaY lq^he_^kkhEoHk_ga`Kqpwcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_@]paPeiaH]op=_perepu@]pawcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_@]paPeiaH]opHk_gkqp@]pawcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_@]paPeiaH]opHkcej@]pawcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_@]paPeiaH]opL]ooskn`?d]jca`@]pawcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_opnejcL]ooskn`Mqaopekjwcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_opnejcL]ooskn`=josanwcap7oap7y W@]p]Iai^anY WTihAhaiajpY lq^he_opnejcQoanJ]iawcap7oap7y W@]p]Iai^anY WTihAhaiajpY
291
292
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
lq^he_opnejcL]ooskn`wcap7oap7y W@]p]Iai^anY WTih=nn]u$AhaiajpJ]ia9Nkhao%(Tih=nn]uEpai$AhaiajpJ]ia9Nkha%Y lq^he_Heop8opnejc:Nkhao w cap7 oap7 y lq^he_^kkh?d]jcaL]ooskn`$opnejckh`L]ooskn`(opnejcjasL]ooskn`% w eb$L]ooskn`*Amq]ho$kh`L]ooskn`%% w L]ooskn`9jasL]ooskn`7 napqnjpnqa7 y napqnjb]hoa7 y lq^he_^kkh?d]jcaL]ooskn`Mqaopekj=j`=josan$opnejcl]ooskn`(± opnejcjasL]ooskn`Mqaopekj(opnejcjasL]ooskn`=josan% w eb$L]ooskn`*Amq]ho$l]ooskn`%% w L]ooskn`Mqaopekj9jasL]ooskn`Mqaopekj7 L]ooskn`=josan9jasL]ooskn`=josan7 napqnjpnqa7 y napqnjb]hoa7 y lq^he_opnejcCapL]ooskn`$opnejcl]ooskn`=josan% w napqnjL]ooskn`7 y lq^he_opnejcNaoapL]ooskn`$% w napqnjL]ooskn`7 y lq^he_opnejcNaoapL]ooskn`$opnejcl]ooskn`=josan% w napqnjL]ooskn`7 y lq^he_^kkhQjhk_gQoan$% w napqnjEoHk_ga`Kqp9b]hoa7 y y This class supports two features. It must be transferred via the service and it is part of the data CONTRACT!DATACONTRACTIN7#&DEFINESTHESTRUCTUREOFCOMPLEXDATA!PPLYINGTHE@]p]?kjpn]_p attributeTOACLASSMAKESTHATCLASSINTOADATACONTRACT%ACHSERIALIZABLEMEMBERINTHEDATACONtract class is tagged with the @]p]Iai^an=ppne^qpa)NTHEEXAMPLE THESAMECLASSISUSEDINTERNALLY TOSERIALIZETHEUSEROBJECTSTOTHE8-,FILE4HETihAhaiajpATTRIBUTESMARKTHEELEMENTSTOBESERIALIZED
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Each user can be the member of none, one, or many roles. The roles are simple strings stored in another collection. To reference the roles in the user object, a copy of the role’s name is stored. A Heop8opnejc:OBJECTISUSEDINTERNALLY(OWEVER IN8-, THEFORMATSHOULDBEMOREREADABLE The Tih=nn]u and Tih=nn]uEpai attributesENSURETHATTHEELEMENTNAMESARECORRECT2EFERTO ,ISTING LATERINTHISCHAPTER TOVIEWTHECREATED8-, )N,ISTING THEHELPERCLASSISSHOWNTOCOMPLETETHECODENEEDEDTOBUILDTHESERVICE Listing 7-6. A Helper Class That Is Used to Access the Data File ejpanj]hop]pe__h]ooBehaI]j]can w lner]pa_kjopopnejc@=P=L=PD9=ll[@]p]XXQoan@]p]*tih7 lner]paop]pe_na]`kjhuopnejc`]p]L]pd7 lner]paop]pe_k^fa_phk_gan9jask^fa_p$%7 op]pe_BehaI]j]can$% w Qne_k`aQne9jasQne$pulakb$Qoan@]p]%*=ooai^hu*?k`a>]oa%7 `]p]L]pd9L]pd*?ki^eja$@ena_pknu*CapL]najp$± L]pd*Cap@ena_pknuJ]ia$_k`aQne*Hk_]hL]pd%%*BqhhJ]ia(@=P=L=PD%7 ++_da_gbehalanieooekjo BehaEKLanieooekjlanieooekj9jasBehaEKLanieooekj$± BehaEKLanieooekj=__aoo*=hh=__aoo(`]p]L]pd%7 lanieooekj*@ai]j`$%7 y ejpanj]hop]pe_Qoan@]p]Hk]`$% w hk_g$hk_gan% w TihOane]hevanto9jasTihOane]hevan$pulakb$Qoan@]p]%%7 Qoan@]p]q`9jqhh7 pnu w qoejc$BehaOpna]ibo9jasBehaOpna]i$`]p]L]pd(BehaIk`a*Klaj%% w q`9to*@aoane]heva$bo%]oQoan@]p]7 y y _]p_d w ++o]ranq`eiajp]nubkni]p q`9jasQoan@]p]$%7 O]ra$q`%7 y napqnjq`7 y y ejpanj]hop]pe_rke`O]ra$Qoan@]p]q`% w hk_g$hk_gan% w TihOane]hevanto9jasTihOane]hevan$pulakb$Qoan@]p]%%7 qoejc$BehaOpna]ibo9jasBehaOpna]i$`]p]L]pd(BehaIk`a*?na]pa%% w
293
294
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
to*Oane]heva$bo(q`%7 y y y ejpanj]hop]pe_opnejc?]h_qh]paOD=-$opnejcpatp% w ^upaWY^qbban9Aj_k`ejc*=O?EE*Cap>upao$patp%7 OD=-?nulpkOanre_aLnkre`an_nulpkPn]jobkniOD=-9± jasOD=-?nulpkOanre_aLnkre`an$%7 opnejcd]od9>ep?kjranpan*PkOpnejc$± _nulpkPn]jobkniOD=-*?kilqpaD]od$^qbban%%7 napqnjd]od7 y y The class is static and provides three methods. With Hk]`, the caller can retrieve the current contents of the stored data. In O]ra THEDATAISWRITTENBACKTODISK"OTHMETHODSBLOCKCONCURRENT threads because this is a multi-threaded environment. The thread handling is in neither way optiMIZEDBUTGIVESYOUTHEIDEAWHATYOUSHOULDBEAWAREOF The ?]h_qh]paOD=- methodCREATESTHE3(!HASHOFAGIVENSTRING)TSUSEDINTERNALLYTOHASH the password. )FEVERYTHINGWORKSASINTENDED THESERVICECREATESA8-,FILESIMILARTOTHEONESHOWNIN Listing 7-7. Listing 7-7. The Data File Filled with Some Users and Roles 8;tihranoekj9-*,;: 8Qoan@]p]tihjo6toe9dppl6++sss*s/*knc+.,,-+TIHO_dai])ejop]j_atihjo6to`9dppl6++sss* s/*knc+.,,-+TIHO_dai]: 8QoanoEo=llnkra`9b]hoaEoHk_ga`Kqp9b]hoa: 8?kiiajp:Paop8+?kiiajp: 8?na]pekj@]pa:.,,5),0)/,P-/6-.6./*2,,4451',.6,,8+?na]pekj@]pa: 8Ai]eh:gn]qoa4)A/)1/)>,)B/8+L]ooskn`: 8Nkhao: 8Nkha:A`epkn8+Nkha: 8+Nkhao: 8+Qoano: 8Nkhao:=`iej8+Nkhao: 8Nkhao:Qoan8+Nkhao: 8Nkhao:?kjpne^qpkn8+Nkhao: 8Nkhao:A`epkn8+Nkhao: 8+Qoan@]p]: The collection of Nkhao elements at the end contains the list of roles used in the provider. In the Qoano element, the Nkhao element contains the assigned roles for that user. The other elements contain the properties. Eo=llnkra` and EoHk_ga`Kqp are stored as attributes because they only contain scalar values. The L]ooskn`ELEMENTDISPLAYSTHEHASHINPLAINTEXTFORMAT )NTHISSECTION WEVELOOKEDATTHEBASICOPERATIONSOFTHEMEMBERSHIPSERVICEANDITSSTORAGE classes. The roles are already in this schema, and the Qoan object accepts setting the assigned roles. All available roles are stored in separate Nkhao items at the end of the file—see the bold elements IN,ISTING &ROMWHEREDOTHEROLESORIGINATE)NTHENEXTSECTION WELLLOOKATA2OLESERVICE)T requires less code, as it reuses portions ofTHE-EMBERSHIPSERVICE
Creating the Role Service The role service isMUCHSIMPLERTHANTHE-EMBERSHIPSERVICE)NSTEADOFDEALINGWITHAh2OLEv object, we’ll simply store roles as strings. The functions of the role service are to assign users to EXISTINGROLES CREATEORREMOVEROLES ANDSEARCHFORUSERSTHATAREMEMBERSOFSPECIFICROLES4HE DATASTORAGEISTHESAMEASFORTHEMEMBERSHIPSERVICE2EFERTOTHE-EMBERSHIPCLASSESTOSEEHOW both parts operate together. 4HE2OLESERVICEREQUIRESTHREECOMPONENTS
s )2OLE3ERVICEINTERFACE
s 2OLE3ERVICESERVICE
s 2OLE3ERVICECLASS
As before, the interface defines the service contract, while the class implements the service. The SERVICEFILEISTHESERVICEEXPOSEDBY7#&ASTHEENDPOINT ANDITREFERSTOTHECLASSSIMPLEMENTATION INACODE BEHINDFILESEE,ISTING
295
296
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Listing 7-8. The Contract Defined by an Interface WOanre_a?kjpn]_pY lq^he_ejpanb]_aENkhaOanre_a w WKlan]pekj?kjpn]_pY rke`=``QoanoPkNkhao$opnejcWYqoanj]iao(opnejcWYnkhaJ]iao%7 WKlan]pekj?kjpn]_pY rke`?na]paNkha$opnejcnkhaJ]ia%7 WKlan]pekj?kjpn]_pY ^kkh@ahapaNkha$opnejcnkhaJ]ia(^kkhpdnksKjLklqh]pa`Nkha%7 WKlan]pekj?kjpn]_pY opnejcWYBej`QoanoEjNkha$opnejcnkhaJ]ia(opnejcqoanj]iaPkI]p_d%7 WKlan]pekj?kjpn]_pY opnejcWYCap=hhNkhao$%7 WKlan]pekj?kjpn]_pY opnejcWYCapNkhaoBknQoan$opnejcqoanj]ia%7 WKlan]pekj?kjpn]_pY opnejcWYCapQoanoEjNkha$opnejcnkhaJ]ia%7 WKlan]pekj?kjpn]_pY ^kkhEoQoanEjNkha$opnejcqoanj]ia(opnejcnkhaJ]ia%7 WKlan]pekj?kjpn]_pY rke`NaikraQoanoBnkiNkhao$opnejcWYqoanj]iao(opnejcWYnkhaJ]iao%7 WKlan]pekj?kjpn]_pY ^kkhNkhaAteopo$opnejcnkhaJ]ia%7 y The service defines a direct mirror of the NkhaLnkre`anCLASS4HISMAKESITEASIERTOCALLTHE service’s methods from the provider shown later. So far, this is very straightforward, as shown in Listing 7-9. Listing 7-9. The Service Class 8!adej`9NkhaOanre_a*or_*_o!: 4HESERVICECLASS*or_FILE POINTSTOTHECODE BEHINDFILECONTAININGTHEIMPLEMENTATIONSEE Listing 7-10). Listing 7-10. The Implementation of the Service lq^he__h]ooNkhaOanre_a6ENkhaOanre_a w lq^he_rke`=``QoanoPkNkhao$opnejcWYqoanj]iao(opnejcWYnkhaJ]iao% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoano9$bnkiqejq`*Qoano
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
sdanaqoanj]iao*?kjp]ejo$q*QoanJ]ia% oaha_pq%7 bkna]_d$Qoanqoanejqoano% w qoan*Nkhao*Naikra=hh$nkha9:nkhaJ]iao*?kjp]ejo$nkha%%7 qoan*Nkhao*=``N]jca$nkhaJ]iao%7 y BehaI]j]can*O]ra$q`%7 y lq^he_rke`?na]paNkha$opnejcnkhaJ]ia% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 eb$q`*Nkhao*?kjp]ejo$nkhaJ]ia%% w q`*Nkhao*=``$nkhaJ]ia%7 BehaI]j]can*O]ra$q`%7 y y lq^he_^kkh@ahapaNkha$opnejcnkhaJ]ia(^kkhpdnksKjLklqh]pa`Nkha% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoanEjNkha9bnkiqejq`*Qoano sdanaq*Nkhao*?kjp]ejo$nkhaJ]ia% oaha_pq7 eb$qoanEjNkha*?kqjp$%:,""pdnksKjLklqh]pa`Nkha% w napqnjb]hoa7 y q`*Nkhao*Naikra$nkhaJ]ia%7 BehaI]j]can*O]ra$q`%7 napqnjpnqa7 y lq^he_opnejcWYBej`QoanoEjNkha$opnejcnkhaJ]ia(opnejcqoanj]iaPkI]p_d% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nqoanEjNkha9bnkiqejq`*Qoano sdanaq*QoanJ]ia*?kjp]ejo$qoanj]iaPkI]p_d%""± q*Nkhao*?kjp]ejo$nkhaJ]ia% oaha_pq*QoanJ]ia7 napqnjqoanEjNkha*Pk=nn]u8opnejc:$%7 y lq^he_opnejcWYCap=hhNkhao$% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nnkhao9bnkinejq`*Nkhaooaha_pn7 napqnjnkhao*Pk=nn]u8opnejc:$%7 y lq^he_opnejcWYCapNkhaoBknQoan$opnejcqoanj]ia% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nnkhao9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia% oaha_pq*Nkhao%*Benop$%7
297
298
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
napqnjnkhao*Pk=nn]u$%7 y lq^he_opnejcWYCapQoanoEjNkha$opnejcnkhaJ]ia% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nnkhao9$bnkiqejq`*Qoano sdanaq*Nkhao*?kjp]ejo$nkhaJ]ia% oaha_pq*QoanJ]ia%7 napqnjnkhao*Pk=nn]u$%7 y lq^he_^kkhEoQoanEjNkha$opnejcqoanj]ia(opnejcnkhaJ]ia% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nnkhao9$bnkiqejq`*Qoano sdanaq*QoanJ]ia*Amq]ho$qoanj]ia%""q*Nkhao*?kjp]ejo$nkhaJ]ia% oaha_pq*QoanJ]ia%7 napqnjnkhao*?kqjp$%:,7 y lq^he_rke`NaikraQoanoBnkiNkhao$opnejcWYqoanj]iao(opnejcWYnkhaJ]iao% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nnkhao9$bnkiqejq`*Qoano sdanaqoanj]iao*?kjp]ejo$q*QoanJ]ia%""± nkhaJ]iao*Ejpanoa_p$q*Nkhao%*?kqjp$%:, oaha_pq*QoanJ]ia%7 BehaI]j]can*O]ra$q`%7 y lq^he_^kkhNkhaAteopo$opnejcnkhaJ]ia% w Qoan@]p]q`9BehaI]j]can*Hk]`$%7 r]nnkhao9$bnkinejq`*Nkhao sdanan*Amq]ho$nkhaJ]ia% oaha_pn%7 napqnjnkhao*?kqjp$%:,7 y y Again, LINQ is used to access the data store. As the Qoan@]p] class already covers the roles by providing the Nkhao element, the whole NkhaOanre_a class doesn’t require its own data access. The BehaI]j]can*O]ra method is called for methods that create roles data.
Configuring the Services /NCETHESERVICESHAVEBEENDEFINED THEYMUSTBECONFIGURED7#&HASITSOWNDEFINITIONLANGUAGE WHICHEXTENDSTHEweb.config file. The definition has three parts, but some settings can be omitted IFTHEDEFAULTVALUESARESUITABLE7#&ISBASEDONTHE!"#MNEMONIC, which stands for Address, "INDING AND#ONTRACT#ONTRACTISDEFINEDBYADDINGSPECIFICATTRIBUTESTOACLASSORINTERFACEDEFInition, as shown earlier. The binding and address are defined in the configuration. The address defines where a client can reach the service. The binding defines the protocol used between client and service. A complete description consisting of address and binding is also called an endpoint. !SAMPLE8-,CONFIGURATIONISSHOWNIN,ISTING
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Listing 7-11. The Configuration of the Service 8ouopai*oanre_aIk`ah: 8oanre_ao: 8oanre_a^ad]rekn?kjbecqn]pekj9Iai^anodelOanre_a>ad]rekn± j]ia9=lnaoo*Atpajoe^ehepu*Iai^anodel*Iai^anodelOanre_a: 8aj`lkejp]``naoo9^ej`ejc9soDppl>ej`ejc± _kjpn]_p9=lnaoo*Atpajoe^ehepu*Iai^anodel*EIai^anodelOanre_a: 8e`ajpepu: 8`jor]hqa9hk_]hdkop+: 8+e`ajpepu: 8+aj`lkejp: 8aj`lkejp]``naoo9iat^ej`ejc9iatDppl>ej`ejc± _kjpn]_p9EIap]`]p]At_d]jca+: 8dkop: 8^]oa=``naooao: 8]``^]oa=``naoo9dppl6++hk_]hdkop+oanre_a+: 8+^]oa=``naooao: 8+dkop: 8+oanre_a: 8oanre_a^ad]rekn?kjbecqn]pekj9NkhaOanre_a>ad]rekn± j]ia9=lnaoo*Atpajoe^ehepu*Iai^anodel*NkhaOanre_a: 8aj`lkejp]``naoo9^ej`ejc9soDppl>ej`ejc± _kjpn]_p9=lnaoo*Atpajoe^ehepu*Iai^anodel*ENkhaOanre_a: 8e`ajpepu: 8`jor]hqa9hk_]hdkop+: 8+e`ajpepu: 8+aj`lkejp: 8aj`lkejp]``naoo9iat^ej`ejc9iatDppl>ej`ejc± _kjpn]_p9EIap]`]p]At_d]jca+: 8+oanre_a: 8+oanre_ao: 8^ad]rekno: 8oanre_a>ad]rekno: 8^ad]reknj]ia9Iai^anodelOanre_a>ad]rekn: 8oanre_aIap]`]p]dpplCapAj]^ha`9pnqa+: 8oanre_a@a^qcej_hq`aAt_alpekj@ap]ehEjB]qhpo9pnqa+: 8+^ad]rekn: 8^ad]reknj]ia9NkhaOanre_a>ad]rekn: 8oanre_aIap]`]p]dpplCapAj]^ha`9pnqa+: 8oanre_a@a^qcej_hq`aAt_alpekj@ap]ehEjB]qhpo9b]hoa+: 8+^ad]rekn: 8+oanre_a>ad]rekno: 8+^ad]rekno: 8+ouopai*oanre_aIk`ah: The ouopai*oanre_aIk`ah sectionCONTAINSALLTHESETTINGSREQUIREDBYTHE7#&SERVICES)TS DEFINITIONCONSISTSOFTWOSERVICESANDTWOCORRESPONDINGBEHAVIORS"ECAUSEAREGULAR7EB3ERVICE is required, the soDppl>ej`ejcISUSED!NADDITIONALENDPOINTISDEFINEDFORMETADATAEXCHANGE using the iatDppl>ej`ejc4HISALLOWSTHECLIENTTOCREATEAPROXYTHATTALKSTOTHESERVICE4HECLIENT ISTECHNICALLYCREATEDWHENYOUREFERENCETHESERVICEIN6ISUAL3TUDIO 7HENTHISOCCURS THEMETA data endpoint is called to retrieve the service’s description. This includes all methods, signatures, and data objects used by the service, as well as the endpoint configuration. )FYOUUSEANDDEBUGTHEWHOLESOLUTION YOUMIGHTENCOUNTERANEXCEPTION&ORSECURITYREASONS THEDETAILSOFANEXCEPTIONARENOTTRANSFERREDTOTHECLIENT ASINTRUDERSCOULDTRYTOPROVOKE ANERRORBYATTACKINGTHESERVICE)FTHEINTERNALLYTHROWNEXCEPTIONEXPOSESDETAILS THEHACKERSWILL
299
300
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
RECEIVEINFORMATIONWITHWHICHTHEYCANREFINETHEIRATTACK$URINGTHEDEVELOPMENTCYCLE however, you’ll need as much information as possible in order to troubleshoot errors. Set the ej_hq`aAt_alpekj@ap]ehEjB]qhpo attributeTOhTRUEvWHILEDEBUGGINGANDCHANGEITTOhFALSEv before publishing the service. .OWTHESERVICEISUPANDRUNNING9OUCANCHECKTHISBYLAUNCHINGTHE*or_ endpoints in the BROWSER*USTRIGHT CLICKTHESERVICEANDCHOOSEView in browser. You should see a service description similar to that shown in Figure 7-5.
Figure 7-5. The service is up and running. 5SETHESAMEPROCEDURETOCHECKTHE2OLE3ERVICE
Implementing the Provider The provider requires a few more steps: 1. 2EFERENCINGTHESERVICES 2. )MPLEMENTINGTHE-EMBERSHIPPROVIDER 3. )MPLEMENTINGTHE2OLEPROVIDER 4. #ONFIGURINGTHE7EBPROJECTSOTHATITACCEPTSTHEPROVIDERS The reference is easy. Simply choose Add service referenceFROMTHECONTEXTMENUOFYOURPROJECT4HENCLICKONDiscover and Services in Solution SEE&IGURE
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Figure 7-6. Adding the service references /PENTHESERVICEINTHETREEVIEWTOTHELEFTANDCHOOSETHECONTRACT'IVETHESERVICEASUITABLE NAME)NTHEEXAMPLE )VEUSEDIai^anodelOanre_a and NkhaOanre_a RESPECTIVELY2EPEATTHESTEPS for the other service. You now have two service references in your project.
Provider-Specific Configuration 4HEPROVIDERCANBECOMBINEDWITHITSOWNCONFIGURATIONTOSUPPORTSPECIFICSETTINGS2EFERTO #HAPTERFORMOREINFORMATIONABOUTTHEBASICSTEPSOFPROVIDERIMPLEMENTATION&ORTHESAKEOF BREVITY )HAVEREMOVEDFROMTHEEXAMPLEANYCODENOTREQUIREDFORBASICTASKS
Create the Membership Provider 4HE-EMBERSHIPPROVIDERIMPLEMENTSTHEIai^anodelLnkre`an base class. To avoid confusion I named the class SOIai^anodelLnkre`an WHERETHE73PREFIXREMINDSUSTHATITSBASEDONA7EB 3ERVICESEE,ISTING Listing 7-12. The WSMembershipProvider lq^he__h]ooSOIai^anodelLnkre`an6Iai^anodelLnkre`an w lner]paIai^anodelOanre_a?heajp_heajp7 lner]pa^kkh[aj]^haL]ooskn`Naoap7 lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(± Ouopai*?khha_pekjo*Ola_e]heva`*J]iaR]hqa?khha_pekj_kjbec% w eb$_kjbec99jqhh%
301
302
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
pdnksjas=ncqiajpJqhhAt_alpekj$_kjbec%7 eb$Opnejc*EoJqhhKnAilpu$j]ia%% w j]ia9pdeo*CapPula$%*J]ia7 y eb$Opnejc*EoJqhhKnAilpu$_kjbecW`ao_nelpekjY%% w _kjbec*Naikra$`ao_nelpekj%7 _kjbec*=``$`ao_nelpekj(SO>]oa`Iai^anodelLnkre`an%7 y ^]oa*Ejepe]heva$j]ia(_kjbec%7 _heajp9jasIai^anodelOanre_a?heajp$%7 ++klpekj]hl]n]iapano eb$Opnejc*EoJqhhKnAilpu$_kjbecWAj]^haL]ooskn`NaoapY%% w [aj]^haL]ooskn`Naoap9>kkha]j*L]noa$_kjbecWAj]^haL]ooskn`NaoapY%7 _kjbec*Naikra$Aj]^haL]ooskn`Naoap%7 y ++i]j`]pknul]n]iapano =llhe_]pekjJ]ia9_kjbecW=llhe_]pekjJ]iaY7 y lq^he_kranne`aopnejc=llhe_]pekjJ]ia w cap7 oap7 y lq^he_kranne`a^kkh?d]jcaL]ooskn`$opnejcqoanj]ia(opnejckh`L]ooskn`(± opnejcjasL]ooskn`% w napqnj_heajp*?d]jcaL]ooskn`$qoanj]ia(kh`L]ooskn`(jasL]ooskn`%7 y lq^he_kranne`a^kkh?d]jcaL]ooskn`Mqaopekj=j`=josan$opnejcqoanj]ia(± opnejcl]ooskn`(± opnejcjasL]ooskn`Mqaopekj( opnejcjasL]ooskn`=josan% w napqnj_heajp*?d]jcaL]ooskn`Mqaopekj=j`=josan$qoanj]ia(l]ooskn`(± jasL]ooskn`Mqaopekj(± jasL]ooskn`=josan%7 y lq^he_kranne`aIai^anodelQoan?na]paQoan$opnejcqoanj]ia(± opnejcl]ooskn`(± opnejcai]eh(± opnejcl]ooskn`Mqaopekj(± opnejcl]ooskn`=josan(± ^kkheo=llnkra`(± k^fa_plnkre`anQoanGau(± kqpIai^anodel?na]paOp]pqoop]pqo% w Qoanqoan9_heajp*?na]paQoan$kqpop]pqo(qoanj]ia(l]ooskn`(ai]eh(± l]ooskn`Mqaopekj(l]ooskn`=josan(±
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
eo=llnkra`(lnkre`anQoanGau%7 eb$qoan99jqhh%napqnjjqhh7 Iai^anodelQoaniq9jasIai^anodelQoan$pdeo*CapPula$%*J]ia( qoan*QoanJ]ia( lnkre`anQoanGau( qoan*Ai]eh( qoan*L]ooskn`Mqaopekj( ( qoan*Eo=llnkra`( qoan*EoHk_ga`Kqp( qoan*?na]pekj@]pa( qoan*H]opHkcej@]pa( qoan*H]op=_perepu@]pa( qoan*H]opL]ooskn`?d]jca`@]pa( qoan*H]opHk_gkqp@]pa %7 napqnjiq7 y lq^he_kranne`a^kkh@ahapaQoan$opnejcqoanj]ia(^kkh`ahapa=hhNah]pa`@]p]% w napqnj_heajp*@ahapaQoan$qoanj]ia(`ahapa=hhNah]pa`@]p]%7 y lq^he_kranne`a^kkhAj]^haL]ooskn`Naoap w capwnapqnj[aj]^haL]ooskn`Naoap7y y lq^he_kranne`a^kkhAj]^haL]ooskn`Napnear]h w capwpdnksjasJkpEilhaiajpa`At_alpekj$%7y y lq^he_kranne`aIai^anodelQoan?khha_pekjBej`Qoano>uAi]eh$opnejcai]ehPkI]p_d(± ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o% w napqnj?kluPkIai^anodel?khha_pekj$_heajp*Bej`Qoano>uAi]eh$kqppkp]hNa_kn`o(± ai]ehPkI]p_d(l]caEj`at(l]caOeva%%7 y lq^he_kranne`aIai^anodelQoan?khha_pekjBej`Qoano>uJ]ia$opnejcqoanj]iaPkI]p_d( ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o% w napqnj?kluPkIai^anodel?khha_pekj$_heajp*Bej`Qoano>uJ]ia$kqppkp]hNa_kn`o( qoanj]iaPkI]p_d(l]caEj`at(l]caOeva%%7 y lq^he_kranne`aIai^anodelQoan?khha_pekjCap=hhQoano$ejpl]caEj`at(± ejpl]caOeva(kqpejppkp]hNa_kn`o% w napqnj?kluPkIai^anodel?khha_pekj$_heajp*Cap=hhQoano$kqppkp]hNa_kn`o(± l]caEj`at(l]caOeva%%7 y lner]paIai^anodelQoan?khha_pekj?kluPkIai^anodel?khha_pekj$QoanWYqoano% w
303
304
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Iai^anodelQoan?khha_pekjiq_9jasIai^anodelQoan?khha_pekj$%7 bkna]_d$Qoanqoanejqoano% w iq_*=``$?kluPkIai^anodelQoan$qoan%%7 y napqnjiq_7 y lner]paIai^anodelQoan?kluPkIai^anodelQoan$Qoanqoan% w Iai^anodelQoaniq9jasIai^anodelQoan$pdeo*CapPula$%*J]ia( qoan*QoanJ]ia( ( qoan*Ai]eh( qoan*L]ooskn`Mqaopekj( qoan*?kiiajp( qoan*Eo=llnkra`( qoan*EoHk_ga`Kqp( qoan*?na]pekj@]pa( qoan*H]opHkcej@]pa( qoan*H]op=_perepu@]pa( qoan*H]opL]ooskn`?d]jca`@]pa( qoan*H]opHk_gkqp@]pa%7 napqnjiq7 y lq^he_kranne`aejpCapJqi^anKbQoanoKjheja$% w napqnj_heajp*CapJqi^anKbQoanoKjheja$%7 y lq^he_kranne`aopnejcCapL]ooskn`$opnejcqoanj]ia(opnejc]josan% w napqnj_heajp*CapL]ooskn`$qoanj]ia(]josan%7 y lq^he_kranne`aIai^anodelQoanCapQoan$opnejcqoanj]ia(^kkhqoanEoKjheja% w napqnj?kluPkIai^anodelQoan$_heajp*CapQoan^uJ]ia$qoanj]ia(qoanEoKjheja%%7 y lq^he_kranne`aIai^anodelQoanCapQoan$k^fa_plnkre`anQoanGau(± ^kkhqoanEoKjheja% w pdnksjasJkpEilhaiajpa`At_alpekj$%7 y lq^he_kranne`aopnejcCapQoanJ]ia>uAi]eh$opnejcai]eh% w napqnj_heajp*CapQoanJ]ia>uAi]eh$ai]eh%7 y lq^he_kranne`aejpI]tEjr]he`L]ooskn`=ppailpo w capwnapqnj17y y
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
lq^he_kranne`aejpIejNamqena`Jkj=hld]jqiane_?d]n]_pano w capwnapqnj-7y y lq^he_kranne`aejpIejNamqena`L]ooskn`Hajcpd w capwnapqnj27y y lq^he_kranne`aejpL]ooskn`=ppailpSej`ks w capwnapqnj-,7y y lq^he_kranne`aIai^anodelL]ooskn`Bkni]pL]ooskn`Bkni]p w capwnapqnjIai^anodelL]ooskn`Bkni]p*?ha]n7y y lq^he_kranne`aopnejcL]ooskn`OpnajcpdNacqh]nAtlnaooekj w capwnapqnj7y y lq^he_kranne`a^kkhNamqenaoMqaopekj=j`=josan w capwnapqnjb]hoa7y y lq^he_kranne`a^kkhNamqenaoQjemqaAi]eh w capwnapqnjb]hoa7y y lq^he_kranne`aopnejcNaoapL]ooskn`$opnejcqoanj]ia(opnejc]josan% w napqnj_heajp*NaoapL]ooskn`$qoanj]ia(]josan%7 y lq^he_kranne`a^kkhQjhk_gQoan$opnejcqoanJ]ia% w pdnksjasJkpEilhaiajpa`At_alpekj$%7 y lq^he_kranne`arke`Ql`]paQoan$Iai^anodelQoanqoan% w Qoanq9jasQoan$%7 bkna]_d$LnklanpuEjbkleejq*CapPula$%*CapLnklanpeao$%% w LnklanpuEjbkleP]ncap9qoan*CapPula$%*CapLnklanpu$le*J]ia%7 eb$leP]ncap9jqhh% w le*OapR]hqa$q(leP]ncap*CapR]hqa$qoan(jqhh%(jqhh%7 y y
305
306
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
_heajp*Ql`]paQoan$q%7 y lq^he_kranne`a^kkhR]he`]paQoan$opnejcqoanj]ia(opnejcl]ooskn`% w napqnj_heajp*R]he`]paQoan$qoanj]ia(l]ooskn`%7 y y The client is instantiated in the Ejepe]heva method. The provider’s methods subsequently use THECLIENTTOCALLTHEAPPROPRIATEFUNCTIONSREMOTELY4HEONLYISSUEREQUIRINGMOREWORKISTHEFACT that the Iai^anodelQoan class cannot be used directly by the service. The internally employed Qoan CLASSHASASIMILARSTRUCTUREBUTNODEPENDENCIESONTHEUNDERLYINGPROVIDER,ATER IN,ISTING I use reflection to iterate over the properties and, where a property is both public and available in the target class, copy the value from the Iai^anodelQoan class to the Qoan class. This is in the Ql`]paQoan method. The provider also has several hard-coded settings. In a full-fledged application, these would be obtained from web.config!DDITIONALLY INAREAL WORLDEXAMPLE )WOULDSUGGEST separating the data layer into another assembly to get a multi-tier architecture.
Create the Role Provider 4HE2OLEPROVIDERIMPLEMENTSTHENkhaLnkre`an base class. Again, to avoid confusion, our new class is named SONkhaLnkre`an, where the WS reminds us that it’s based on a Web Service. See ,ISTING Listing 7-13. The WSRoleProvider lq^he__h]ooSONkhaLnkre`an6NkhaLnkre`an w lner]paNkhaOanre_a?heajp_heajp7 lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(± Ouopai*?khha_pekjo*Ola_e]heva`*J]iaR]hqa?khha_pekj_kjbec% w eb$_kjbec99jqhh% pdnksjas=ncqiajpJqhhAt_alpekj$_kjbec%7 eb$Opnejc*EoJqhhKnAilpu$j]ia%% w j]ia9pdeo*CapPula$%*J]ia7 y eb$Opnejc*EoJqhhKnAilpu$_kjbecW`ao_nelpekjY%% w _kjbec*Naikra$`ao_nelpekj%7 _kjbec*=``$`ao_nelpekj(SO>]oa`Iai^anodelLnkre`an%7 y ^]oa*Ejepe]heva$j]ia(_kjbec%7 _heajp9jasNkhaOanre_a?heajp$%7 ++i]j`]pknul]n]iapano =llhe_]pekjJ]ia9_kjbecW=llhe_]pekjJ]iaY7 y lq^he_kranne`arke`=``QoanoPkNkhao$opnejcWYqoanj]iao(opnejcWYnkhaJ]iao% w _heajp*=``QoanoPkNkhao$qoanj]iao(nkhaJ]iao%7 y
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
lq^he_kranne`aopnejc=llhe_]pekjJ]ia w cap7 oap7 y lq^he_kranne`arke`?na]paNkha$opnejcnkhaJ]ia% w _heajp*?na]paNkha$nkhaJ]ia%7 y lq^he_kranne`a^kkh@ahapaNkha$opnejcnkhaJ]ia(^kkhpdnksKjLklqh]pa`Nkha% w napqnj_heajp*@ahapaNkha$nkhaJ]ia(pdnksKjLklqh]pa`Nkha%7 y lq^he_kranne`aopnejcWYBej`QoanoEjNkha$opnejcnkhaJ]ia(± opnejcqoanj]iaPkI]p_d% w napqnj_heajp*Bej`QoanoEjNkha$nkhaJ]ia(qoanj]iaPkI]p_d%7 y lq^he_kranne`aopnejcWYCap=hhNkhao$% w napqnj_heajp*Cap=hhNkhao$%7 y lq^he_kranne`aopnejcWYCapNkhaoBknQoan$opnejcqoanj]ia% w napqnj_heajp*CapNkhaoBknQoan$qoanj]ia%7 y lq^he_kranne`aopnejcWYCapQoanoEjNkha$opnejcnkhaJ]ia% w napqnj_heajp*CapQoanoEjNkha$nkhaJ]ia%7 y lq^he_kranne`a^kkhEoQoanEjNkha$opnejcqoanj]ia(opnejcnkhaJ]ia% w napqnj_heajp*EoQoanEjNkha$qoanj]ia(nkhaJ]ia%7 y lq^he_kranne`arke`NaikraQoanoBnkiNkhao$opnejcWYqoanj]iao(± opnejcWYnkhaJ]iao% w _heajp*NaikraQoanoBnkiNkhao$qoanj]iao(nkhaJ]iao%7 y lq^he_kranne`a^kkhNkhaAteopo$opnejcnkhaJ]ia% w napqnj_heajp*NkhaAteopo$nkhaJ]ia%7 y y
307
308
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Configuring the Provider After the provider is implemented, it must be configured. As for any other provider, this is achieved within the web.configFILE!SYOUCANSEEIN,ISTING THESONkhaLnkre`an class is simpler than the provider shown before. The methods predominantly call the corresponding service methods and do not add additional logic. Again, the Ejepe]heva method contains the instantiation of the NkhaOanre_a?heajp class. Listing 7-14. Configuring the Providers 8ouopai*sa^: 8iai^anodel`ab]qhpLnkre`an9SOIai^anodelLnkre`an: 8lnkre`ano: 8]``j]ia9SOIai^anodelLnkre`an pula9=lnaoo*Atpajoe^ehepu*Iai^anodel*SOIai^anodelLnkre`an( =lnaoo*Atpajoe^ehepu*Iai^anodel Aj]^haL]ooskn`Naoap9Pnqa +: 8+lnkre`ano: 8+iai^anodel: 8nkhaI]j]can`ab]qhpLnkre`an9SONkhaLnkre`anaj]^ha`9pnqa: 8lnkre`ano: 8_ha]n+: 8]``j]ia9SONkhaLnkre`an pula9=lnaoo*Atpajoe^ehepu*Iai^anodel*SONkhaLnkre`an( =lnaoo*Atpajoe^ehepu*Iai^anodel+: 8+lnkre`ano: 8+nkhaI]j]can: The provider’s ]`` element requires the j]ia, the pula and optionally the parameters defined in the configuration. The pula is the full class name, including the assembly name. In this case, I’ve USEDAREGULAR6ISUAL3TUDIOPROJECT WHICHCOMPILESINTOA$,,4HENAMEANDNAMESPACEOF the DLL are specified here.
Testing the Providers You can nowUSETHEPROVIDERS(OWEVER YOULLNEEDSEVERALADDITIONALPAGESINORDERTOADDUSERS ORROLES RETRIEVEDATA CHECKUSERS ASSIGNROLES ETC&ORTUNATELY 6ISUAL3TUDIOCONTAINSANEMBEDded tool that allows you to manage users and roles. This tool is available from the Project menu and is called ASP.NET configuration. You can add and change users, add and assign roles, and test all common settings of the new providers without WRITINGASINGLELINEOFCODE/NTHESTARTPAGE CHOOSETHE3ECURITYTABTOVIEWTHECURRENTSTATEOF the providers. If you have already defined some users and roles, their quantities are displayed, as shown in Figure 7-7.
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Figure 7-7. The security tab uses the custom provider if properly configured. You can use the tool to create or modify users and add or assign roles. If the tool does not WORKASEXPECTED CHECKWHETHERORNOTTHECUSTOMPROVIDERSAREPROPERLYREGISTERED/PENTHE0ROVIDERTABANDCLICKONSelect a different provider for each feature (advanced). Your custom providers 73-EMBERSHIP0ROVIDERAND732OLE0ROVIDER SHOULD be selected, as shown in&IGURE
Figure 7-8. The provider tab shows the custom provider if properly configured.
309
310
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Extending Profile Providers The Profile service is an integrated module for storing and retrieving user settings. Settings are not limited to ASP.NET, but are widely used in distributed multi-user environments; even for Win &ORMSAPPLICATION ASIMILARCONCEPTEXISTS4HE0ROFILESERVICEEMPLOYSA0ROFILEPROVIDERTOREAD DATAFROM ANDSAVEDATATO ASTORAGEDEVICE"YDEFAULT THEDATASTOREISA31,3ERVEROR31,3ERVER %XPRESSINSTANCE ANDTHEPROVIDERCLASSISOuopai*Sa^*Lnkbeha*OmhLnkbehaLnkre`an. This provider is ALSORESPONSIBLEFORALLOWINGUSERSTOUPDATETHEIRSETTINGS#OMMONMANAGEMENTFUNCTIONS SUCH as deleting a Profile after a period of inactivity, are located here as well.
NNote
Profile providers are part of the provider model. It is described in detail in Chapter 4.
The Profile Service The purpose of the Profile service is to store and retrieve user settings. With these settings, users can PERSONALIZETHEIRWEBPAGES4YPICALINFORMATIONSTOREDHEREINCLUDESTHEFOLLOWING
s 5SERINFORMATION SUCHASCITYANDPHONENUMBER
s 0REFERENCESFORACCESSIBILITY COLORS ANDFONTSIZE
s $ATARELATEDTOTHECURRENTSESSION SUCHASTHECONTENTSOFASHOPPINGBASKET
s )NDIVIDUALSELECTIONSOFSERVICES SUCHASNEWSFEEDSORNEWSLETTERTOPIC
4HEPERSONALIZATIONPOSSIBILITIESARELIMITLESS)NSTEADOFBUILDINGCUSTOMDATABASEMODULES FROMSCRATCHTOSTORETHEUSERSSETTINGS THISISWHERETHE0ROFILESERVICEEXCELS The services’ definition consists of two parts—Profile properties and related user. The Profile service retrieves properties for the current user from the underlying provider. In order to use Profiles, you must first activate this feature by defining the Provider in the web.configFILE"YDEFAULT the OmhLnkbehaLnkre`an stores properties in the local SQL Server database. The properties are defined in the web.configFILE TOO5NLIKEAPPLICATIONSETTINGS WHEREKEYVALUEPAIRSARESTOREDIN web.config, the Profile property definition consists of the property definition only. Actual values are stored in the Profile provider’s data store.
Understanding the Profile Provider "EFOREYOUCONSIDEREXTENDINGTHE0ROFILEPROVIDER LETSLOOKATTHEFEATURESTHATTHEPROVIDER supports. The LnkbehaLnkre`an class implements the Lnkre`an>]oa base class and the Ouopai* ?kjbecqn]pekj*OappejcoLnkre`an. As the namespace implies, the concepts are not limited to ASP. .%44HE0ROFILEPROVIDEREXTENDSTHESETTINGSCONCEPTWITHSOMEFEATURESTHATALLOWFORTHEMANAGEMENTOFUSER0ROFILES SUCHASDELETINGOF0ROFILESANDACTIVITYMONITORINGSEE4ABLE When implementing a custom Profile provider, it must support as a minimum the preceding methods in order to be used transparently in applications. The Profile data always has a specific scope, which is usually the current user’s username. (OWEVER THE0ROFILEPROVIDERALSOSUPPORTSANONYMOUSUSERS4HISMEANSTHATAUSERNOTCURRENTLY logged in gets a Profile as a non-authenticated user. #ONSIDERAVISITORWHOSELECTSSEVERALITEMSTOPURCHASEATA7EBSITE BUTTHEYHAVENOTYET LOGGEDIN4HEIR0ROFILECONTAINSSEVERALITEMSINTHEIRSHOPPINGBASKET)TISTHEREFOREIMPORTANTTO KEEPTHEIR0ROFILECONTAININGTHEIRSELECTIONS SEPARATEFROMTHE0ROFILESOFOTHERNON AUTHENTICATED users. To accomplish this, the Profile provider assigns each anonymous user an ID that is used as AKEYINTHEABSENCEOFAUSERNAME
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Table 7-3. Methods and Properties Defined by the ProfileProvider Class
Method
Description
Ejepe]heva
Derived from Lnkre`an>]oa#ONTAINSTHECODEFORSETTINGUP the provider and reading the settings.
=llhe_]pekjJ]ia
Derived from OappejcoLnkre`an. Name of the current application. The data store can use this to handle multiple applications that use the same database.
CapLnklanpuR]hqao
Derived from OappejcoLnkre`an2ETRIEVESALISTOFPROPERTIES using a Oappejco?kjpatp object containing information about the user. You could use this to retrieve Profile information FORAUSER4HECONTEXTPROVIDESINFORMATIONABOUTAUTHENTIcated or anonymous users. The method returns an object of type OappejcoLnklanpu?khha_pekj, which is a collection of OappejcoLnklanpu objects, each containing the name and type of a property, as well as additional information such as default SETTINGSANDREAD ONLYSTATE5SINGTHISMETHODUPDATESTHE H]op=_perepu@]paVALUETHATISUSEDTOTRACKACTIVITYANDTHUS monitor logged-on or inactive users).
OapLnklanpuR]hqao
Derived from OappejcoLnkre`an5SESAOappejco?kjpatp object and a OappejcoLnklanpuR]hqa?khha_pekj object to write PROPERTYVALUESBACKTOTHEDATASTORE5SINGTHISMETHODALSO updates the H]op=_perepu@]pa.
@ahapaLnkbehao
5SINGANARRAYOFUSERNAMES THISMETHODDELETESTHERELATED Profiles. An overloaded version of the same method accepts LnkbehaEjbk objects.
@ahapaEj]_peraLnkbehao
Accepts a Lnkbeha=qpdajpe_]pekjKlpekj value and a @]paPeia object in order to delete inactive Profiles. The Profiles should be deleted if the supplied date and time value is equal to or less than the Profile’s H]op=_perepu@]pa.
Cap=hhLnkbehao
2ETRIEVESALLAVAILABLE0ROFILEOBJECTSUSINGA Lnkbeha=qpdajpe_]pekjKlpekj value and an integer VALUEFORTHEPAGEANDTHEMAXIMUMNUMBEROF0ROFILES 4HEPAGEINDEXALLOWSAPAGEDRETRIEVAL4HEMETHODRETURNS a LnkbehaEjbk?khha_pekj with LnkbehaEjbk objects.
Cap=hhEj]_peraLnkbehao
5SINGTHESAMECONDITIONSASCap=hhLnkbehao, this method returns only Profiles that have not been used for a given time.
Bej`Lnkbehao>uQoanJ]ia
2ETRIEVES0ROFILESVIATHEIRUSERNAMES)NDERIVEDCLASSES THIS METHODMIGHTACCEPTWILDCARDS REGULAREXPRESSIONS ORANY OTHERKINDOFSEARCHPATTERNINORDERTORETRIEVEMULTIPLEVALues matching the search string. This method behaves similarly to Cap=hhLnkbehao.
Bej`Ej]_peraLnkbehao>uQoanJ]ia
2ETRIEVESINACTIVE0ROFILESVIATHEIRUSERNAMES)NDERIVED CLASSES THISMETHODMIGHTACCEPTWILDCARDS REGULAREXPRESSIONS ORANYOTHERKINDOFSEARCHPATTERNINORDERTORETRIEVE multiple values matching the search string. This method behaves similarly to Cap=hhLnkbehao.
CapJqi^anKbEj]_peraLnkbehao
2ETRIEVESTHENUMBEROFINACTIVE0ROFILES
311
312
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
When the user leaves the session, the Profile becomes obsolete. This is why the last activity date ISIMPORTANT)FYOUSPECIFYAMINIMUMINACTIVITYTIMEPERIOD THEPROVIDERISABLETODELETEEXPIRED Profiles. The Lnkbeha=qpdajpe_]pekjKlpekj parameter determines which of the anonymous or authenticated Profiles are to be deleted. You can use a different time span for each type. The data store itself is data agnostic—any properties can be stored there. The definition of acceptable values is set in the web.config file for all users.
Serializing and Deserializing 6ALUESFORCUSTOMSETTINGSCANSUPPORTSEVERALDATATYPES-OSTSTORAGESYSTEMSREQUIRESERIALIZED VALUESINSTEADOF.%4OBJECTS&ORTUNATELY THEBASECLASSPROVIDESTHECODEFORSERIALIZINGANDDESERIALIZINGOBJECTS(OWEVER WHENRETRIEVINGVALUES THISCODEISNOTCALLEDAUTOMATICALLY#ONSIDER THECASEWHEREYOUHAVESEVERALSETTINGS ANDDESERIALIZINGTHEMTAKESSOMETIME)FTHECALLINGPARTY REQUIRESONLYONEORTWOOFTHESEVALUES DESERIALIZINGALLOFTHEMWILLWASTERESOURCES4OIMPROVE PERFORMANCE YOUCANCALLTHEBASEMETHODSTODESERIALIZETHEDESIREDPROPERTIESANDIGNOREALLTHE others. This method isKNOWNAShLAZYDESERIALIZATIONvSEE,ISTING Listing 7-15. Typical Strategy for Retrieving and Deserializing Values OappejcoLnklanpuR]hqa?khha_pekjoappejco9jasOappejcoLnklanpuR]hqa?khha_pekj$%7 bkna]_d$OappejcoLnklanpulnklanpuejlnklanpeao% w OappejcoLnklanpuR]hqall9jasOappejcoLnklanpuR]hqa$lnklanpu%7 k^fa_pr]h9CapLnklanpuR]hqaBnki@]p]Okqn_a$lnklanpu*J]ia%7 eb$r]h99jqhh% w ll*LnklanpuR]hqa9jqhh7 ll*@aoane]heva`9pnqa7 ll*Eo@enpu9b]hoa7 y ahoa w ll*LnklanpuR]hqa9@aoane]heva$r]h%7 ll*@aoane]heva`9pnqa7 ll*Eo@enpu9b]hoa7 y oappejco*=``$ll%7 y napqnjoappejco7
Defining Profile Settings A typical settingDEFINITIONLOOKSLIKETHECODEIN,ISTING Listing 7-16. Define Available Properties 8lnkbeha: 8lnklanpeao: 8]``j]ia9Cnaapejcpula9Opnejc+: 8]``j]ia9?kqjppula9Ejp/.`ab]qhpR]hqa9,+: 8+lnklanpeao: 8+lnkbeha:
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
The name of the property and the type are required. A default value can also be specified, which the provider will use if the data store does not retrieve one. 4HESERIALIZATIONCANBEREFINEDBYADDINGTHEoane]heva=o attribute to the Profile property defiNITIONSHOWNINTHENEXTSECTION4HISATTRIBUTEALLOWSTHEPRE SELECTIONOFAPREDEFINEDSERIALIZER
s 3TRING
s "INARY
s 8ML
s 0ROVIDER3PECIFIC
The provider can also be selected on a per-property basis. This allows for different data stores for different types of data. Each property can be limited to authenticated users only by setting the attribute ]hhks=jkjuikqo to b]hoa. &ORMORECOMPLEXSCENARIOS YOUCANGROUPSETTINGSWITHTHE8cnkql: tag. If you plan on supporting hundreds of settings, groups can help you to manage your settings.
Using the Profile Data There are twoWAYSOFREACHING0ROFILEDATAFROMWITHINYOURCODE)NA7EBAPPLICATIONPROJECTAS opposed to a Web site project), the Lnkbeha class is not auto-generated from the current configuration. You will need to use the following two techniques to manage your Profile settings. To retrieve values, use the following pattern: opnejciuR]hqa9$opnejc%?kjpatp*Lnkbeha*CapLnklanpuR]hqa$IuR]hqa%7 Setting values is similarly straightforward: eb$?kjpatp*Lnkbeha*QoanJ]ia9jqhh%w ?kjpatp*Lnkbeha*OapLnklanpuR]hqa$IuR]hqa(okiapdejc%7 ?kjpatp*Lnkbeha*O]ra$%7 y )NA7EBSITEPROJECT 6ISUAL3TUDIOGENERATESALnkbeha class from the configuration settings, WHICHEXPOSESTHE0ROFILEPROPERTIESASTYPEDPROPERTIES opnejciuR]hqa9Lnkbeha*IuR]hqa7 Several free tools are available for overcoming the limitation of Web application projects that DOESNTSUPPORT0ROFILECREATION4HE7EB0ROFILE"UILDERFOUNDATdppl6++_k`a*io`j*ie_nkokbp* _ki+Sa^Lnkbeha>qeh`anISAGOODSTARTINGPOINT(OWEVER DIGGINGDEEPERINTOTHEBUILDTECHNIQUES USEDIN6ISUAL Studio is beyondTHESCOPEOFTHISBOOK
NNote
To overcome the limitations of Web application projects, in this chapter I use the Web site project template. This is the opposite of what I recommend in other parts of the book. As long as you’re not using Profiles, you should stay with Web application projects. If you do use Profiles, it depends on whether strongly typed access is important.
313
314
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Configuring Custom Profile Providers The Profile settings use the embedded provider if no other is specified. The settings follow the SCHEMAFORPROVIDERSEXPLAINEDIN#HAPTER,ISTING SHOWSHOWTODEFINESETTINGSANDHOWTO configure the provider typically. Listing 7-17. Define Available Properties 8lnkbeha`ab]qhpLnkre`an9IuLnkbehaLnkre`an: 8lnklanpeao: 8]``j]ia9BenopJ]iapula9Ouopai*Opnejc+: 8]``j]ia9H]opJ]iapula9Ouopai*Opnejc+: 8]``j]ia9Ai]eh=``naoopula9Ouopai*Opnejc+: 8]``j]ia9I]gaJ]iaLq^he_pula9Ouopai*>kkha]j+: 8]``j]ia9JasoBaa`pula9Ouopai*Opnejc+: 8+lnklanpeao: 8lnkre`ano: 8]``j]ia9IuLnkbehaLnkre`an pula9=lnaoo*Atpajoe^ehepu*TihLnkbehaLnkre`an+: 8+lnkre`ano: 8+lnkbeha: To implement the custom Profile provider, select the preferred data access method.
Implementing a Custom Profile Provider The custom Profile provider shown inTHEFOLLOWINGEXAMPLEUSESAN8-,FILETOSTOREALLUSERSDATA )TDOESNOTIMPLEMENTUSAGEFUNCTIONSSUCHASCHECKINGINACTIVITYORDELETING0ROFILES(OWEVER IT DOESIMPLEMENTTHESAVEANDLOADFUNCTIONSASWELLASTHEUNDERLYINGSERIALIZATIONANDDESERIALIZAtion methods. A Profile provider requires several steps to run properly. Even if only one provider class is necessary for retrieving the code, you need several users and authentication capabilities in order to CHECK the provider.
Preparation Steps To test a custom provider, which requires the ability to log in and log out of different user accounts, I set up Forms Authentication in web.config. Furthermore, I defined the users’ credentials directly in web.config and force the Iai^anodel provider to use data from that file instead of from the default SQL Server. This is accomplished by handling the authentication event manually. The web.config sectionDEFININGTHEUSERSLOOKSLIKETHECODEIN,ISTING Listing 7-18. Define Several Users to Test the Application 8]qpdknev]pekj: 8`ajuqoano9;+: 8+]qpdknev]pekj: 8]qpdajpe_]pekjik`a9Bknio: 8bknio: 8_na`ajpe]hol]ooskn`Bkni]p9?ha]n: 8qoanj]ia9Qoan-l]ooskn`9Qoan-+: 8qoanj]ia9Qoan.l]ooskn`9Qoan.+: 8qoanj]ia9Qoan/l]ooskn`9Qoan/+: 8+_na`ajpe]ho: 8+bknio: 8+]qpdajpe_]pekj:
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
NCaution Storing user credentials in clear text format in web.config is not intended to run in a production environment. It is here only in order to rapidly set up a test environment. .EXT THEAPPLICATIONREQUIRESATLEASTONEPAGEFORCHANGINGANDUSING0ROFILESETTINGS PLUS a login page. The login page consists of a single Hkcej control and an event handler, as shown in ,ISTINGS AND Listing 7-19. The “Core” Component of the Login Page 8^k`u: 8bknie`9bkni-nqj]p9oanran: 8`er: 8]ol6HkcejE@9Hkcej-nqj]p9oanrankj]qpdajpe_]pa9Hkcej-[=qpdajpe_]pa: 8+]ol6Hkcej: 8^n+: Jkpuapnaceopana`;?na]pajas]__kqjp8]ol6DulanHejgE@9DulanHejg- nqj]p9oanranJ]rec]paQnh9z+?na]paQoan*]olt:dana8+]ol6DulanHejg:* 8+`er: 8+bkni: 8+^k`u:
Listing 7-20. An Event Handler Forces the Login Control to Use the Configured User Names lq^he_l]npe]h_h]ooHkcej6Ouopai*Sa^*QE*L]ca w lnkpa_pa`rke`L]ca[Hk]`$k^fa_poaj`an(Arajp=ncoa% w y lnkpa_pa`rke`Hkcej-[=qpdajpe_]pa$k^fa_poaj`an(=qpdajpe_]paArajp=ncoa% w a*=qpdajpe_]pa`9Bknio=qpdajpe_]pekj*=qpdajpe_]pa$Hkcej-*QoanJ]ia(± Hkcej-*L]ooskn`%7 y y 4HENEXTPAGEISALSOFORTESTINGPURPOSES)TCONTAINSASIMPLEFORMTOSAVENEW0ROFILESETTINGS DISPLAYCURRENTSETTINGS ANDCHANGEACCOUNTSSEE,ISTING Listing 7-21. ASPX Page Used to Test the Profile Provider 8^k`u: 8bknie`9bkni-nqj]p9oanran: 8^n+:OapukqnLnkbeha`]p]( 8]ol6HkcejJ]iaE@9HkcejJ]ia-nqj]p9oanran+: 68^n+: 8^n+:A)i]eh6 8]ol6Patp>ktnqj]p9oanranE@9ptpAi]eh:8+]ol6Patp>kt: 8^n+:Bkna?khkn6 8]ol6@nkl@ksjHeopE@9`nlBkna?khknnqj]p9oanran: 8]ol6HeopEpai:Na`8+]ol6HeopEpai: 8]ol6HeopEpai:Cnaaj8+]ol6HeopEpai: 8]ol6HeopEpai:>hqa8+]ol6HeopEpai: 8]ol6HeopEpai:8+]ol6HeopEpai:
315
316
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
8+]ol6@nkl@ksjHeop: 8^n+:>]_g?khkn68]ol6@nkl@ksjHeopE@9`nl>]_g?khknnqj]p9oanran: 8]ol6HeopEpai:Sdepa8+]ol6HeopEpai: 8]ol6HeopEpai:>aeca8+]ol6HeopEpai: 8]ol6HeopEpai:Uahhks8+]ol6HeopEpai: 8+]ol6@nkl@ksjHeop: 8^n+: 8^n+: 8]ol6>qppkjE@9^pjOaj`nqj]p9oanrankj_he_g9^pjOaj`[?he_g Patp9OapLnkbeha@]p]+: 8^n+: 8^n+: Hkckqppkqoa]jkjuikqoik`a(hkcej]o`ebbanajpqoanpkpaop`ebbanajp oappejco6 8]ol6HkcejOp]pqoE@9HkcejOp]pqo-nqj]p9oanran+: "j^ol7$Lna`abeja`68e:Qoan-8+e:(8e:Qoan.8+e:(8e:Qoan/8+e:(eja]_d_]oaqoapda j]ia]ol]ooskn`(pkk%8^n+: 8^n+:Naoqhpkbukqn_qnnajpoappejco68^n+: 8`er: 8]ol6L]jahE@9L]jahOappejconqj]p9oanran: Pdeol]jah"/57o`aoecjeona]`bnki_qnnajpqoan"/57oLnkbeha* 8+]ol6L]jah: 8+`er: 8+bkni: 8+^k`u: The page allows you to set an email address and a color. The color type is used here to demonSTRATESERIALIZATIONOFNON SCALARVALUESSEE&IGURE
Figure 7-9. A simple form to set and retrieve Profile data 4HEPAGESHOWSTHECURRENTLYLOGGED INUSERANDTHESETTINGSTHATFORMATA0ANELCONTROLSEE ,ISTING 4HECODE BEHINDSECTIONSHOWSHOWTHE0ROFILEPROVIDERISINVOKED Listing 7-22. Direct Calls to the Profile Provider and Typed Access lq^he_l]npe]h_h]oo[@ab]qhp6Ouopai*Sa^*QE*L]ca w lnkpa_pa`rke`L]ca[Hk]`$k^fa_poaj`an(Arajp=ncoa% w OapLnkbeha@]p]$%7 y
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
lnkpa_pa`rke`^pjOaj`[?he_g$k^fa_poaj`an(Arajp=ncoa% w Lnkbeha*Bkna?khkn9?khkn*BnkiJ]ia$`nlBkna?khkn*Oaha_pa`R]hqa%7 Lnkbeha*>]_g?khkn9?khkn*BnkiJ]ia$`nl>]_g?khkn*Oaha_pa`R]hqa%7 Lnkbeha*Qoan*Ai]eh9ptpAi]eh*Patp7 OapLnkbeha@]p]$%7 y lner]parke`OapLnkbeha@]p]$% w L]jahOappejco*>]_g?khkn9Lnkbeha*>]_g?khkn7 L]jahOappejco*Bkna?khkn9Lnkbeha*Bkna?khkn7 H]^ahh9jasH]^ah$%7 h*Patp9Lnkbeha*Qoan*Ai]eh7 L]jahOappejco*?kjpnkho*=``$h%7 y y The Lnkbeha classSHOWNIN,ISTING ISAUTO GENERATEDANDDERIVESFROMTHELnkbeha>]oa. It provides typed access to properties. The Profile provider returns settings of type k^fa_p and the access layer casts these types to the final values. Listing 7-23. An Auto-Generated Profile Class ++)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ++8]qpk)cajan]pa`: ++Pdeo_k`as]ocajan]pa`^u]pkkh* ++NqjpeiaRanoekj6.*,*1,3.3*/,30 ++ ++?d]jcaopkpdeobehai]u_]qoaej_knna_p^ad]rekn]j`sehh^ahkopeb ++pda_k`aeonacajan]pa`* ++8+]qpk)cajan]pa`: ++)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) qoejcOuopai7 qoejcOuopai*Sa^7 qoejcOuopai*Sa^*Lnkbeha7 lq^he__h]ooLnkbehaCnkqlQoan6Ouopai*Sa^*Lnkbeha*LnkbehaCnkql>]oaw lq^he_renpq]hopnejcJ]iaw capw napqnj$$opnejc%$pdeo*CapLnklanpuR]hqa$J]ia%%%7 y oapw pdeo*OapLnklanpuR]hqa$J]ia(r]hqa%7 y y lq^he_renpq]hopnejcAi]ehw capw napqnj$$opnejc%$pdeo*CapLnklanpuR]hqa$Ai]eh%%%7 y oapw pdeo*OapLnklanpuR]hqa$Ai]eh(r]hqa%7 y y
317
318
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
y lq^he__h]ooLnkbeha?kiikj6Ouopai*Sa^*Lnkbeha*Lnkbeha>]oaw lq^he_renpq]hejpOevaw capw napqnj$$ejp%$pdeo*CapLnklanpuR]hqa$Oeva%%%7 y oapw pdeo*OapLnklanpuR]hqa$Oeva(r]hqa%7 y y lq^he_renpq]hOuopai*@n]sejc*?khknBkna?khknw capw napqnj$$Ouopai*@n]sejc*?khkn%$pdeo*CapLnklanpuR]hqa$Bkna?khkn%%%7 y oapw pdeo*OapLnklanpuR]hqa$Bkna?khkn(r]hqa%7 y y lq^he_renpq]hOuopai*@n]sejc*?khkn>]_g?khknw capw napqnj$$Ouopai*@n]sejc*?khkn%$pdeo*CapLnklanpuR]hqa$>]_g?khkn%%%7 y oapw pdeo*OapLnklanpuR]hqa$>]_g?khkn(r]hqa%7 y y lq^he_renpq]hLnkbehaCnkqlQoanQoanw capw napqnj$$LnkbehaCnkqlQoan%$pdeo*CapLnkbehaCnkql$Qoan%%%7 y y lq^he_renpq]hLnkbeha?kiikjCapLnkbeha$opnejcqoanj]ia%w napqnj$$Lnkbeha?kiikj%$Lnkbeha>]oa*?na]pa$qoanj]ia%%%7 y y 4OUNDERSTANDTHESESETTINGS TAKEALOOKATTHEweb.config file where the properties are defined, ASSHOWNIN,ISTING Listing 7-24. Definition of Profile Properties 8lnkbeha]qpki]pe_O]raAj]^ha`9pnqa`ab]qhpLnkre`an9TihLnkbehaLnkre`an: 8lnklanpeao: 8cnkqlj]ia9Qoan: 8]``j]ia9J]iapula9Ouopai*Opnejc+: 8]``j]ia9Ai]ehpula9Ouopai*Opnejc+: 8+cnkql: 8]``j]ia9Oevapula9Ouopai*Ejp/.+: 8]``j]ia9Bkna?khknpula9Ouopai*@n]sejc*?khkn+: 8]``j]ia9>]_g?khknpula9Ouopai*@n]sejc*?khkn+: 8+lnklanpeao:
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
8lnkre`ano: 8_ha]n+: 8]``j]ia9TihLnkbehaLnkre`an pula9=lnaoo*Atpajoe^ehepu*LnkbehaLnkre`an*TihLnkbehaLnkre`an+: 8+lnkre`ano: 8+lnkbeha: 4HESETTINGSFORUSERNAMEANDEMAILAREGROUPEDBYTHEh5SERvGROUPELEMENT4HISISMERELY FORORGANIZATIONALPURPOSES ANDHASNODIRECTINFLUENCEONTHEBEHAVIOROFTHEPROVIDER4HE 8lnkre`ano: section contains the definition of the provider’s type. The name is used to set this provider as the default one.
Implementing the Provider You now have the requisite components. The last step is to construct the custom Profile provider ITSELF ASSHOWNIN,ISTING !NEXPLANATIONFOLLOWSTHECODE Listing 7-25. A Custom Profile Provider That Can Save and Retrieve Settings qoejcOuopai7 qoejcOuopai*?khha_pekjo*Cajane_7 qoejcOuopai*?kilkjajpIk`ah7 qoejcOuopai*?kjbecqn]pekj7 qoejcOuopai*?kjbecqn]pekj*Lnkre`an7 qoejcOuopai*EK7 qoejcOuopai*Hejm7 qoejcOuopai*Oa_qnepu*Lanieooekjo7 qoejcOuopai*Patp7 qoejcOuopai*Sa^7 qoejcOuopai*Sa^*Lnkbeha7 qoejcOuopai*Tih*Hejm7 j]iaol]_a=lnaoo*Atpajoe^ehepu*LnkbehaLnkre`an w WOa_qnepuLanieooekj$Oa_qnepu=_pekj*=ooanp( Bh]co9Oa_qnepuLanieooekjBh]c*Oane]hev]pekjBkni]ppan%Y lq^he__h]ooTihLnkbehaLnkre`an6Ouopai*Sa^*Lnkbeha*LnkbehaLnkre`an w lner]pa_kjopopnejc@=P=L=PD9z+=ll[@]p]+Lnkbeha[@]p]7 lq^he_kranne`aopnejc=llhe_]pekjJ]ia w capwpdnksjasJkpOqllknpa`At_alpekj$%7y oapwpdnksjasJkpOqllknpa`At_alpekj$%7y y lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(± Ouopai*?khha_pekjo*Ola_e]heva`*J]iaR]hqa?khha_pekj_kjbec% w ^]oa*Ejepe]heva$j]ia(_kjbec%7 eb$_kjbec*?kqjp:,% pdnksjasLnkre`anAt_alpekj$Qjna_kcjeva`]ppne^qpa6'± _kjbec*CapGau$,%%7 y
319
320
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
lq^he_kranne`aOuopai*?kjbecqn]pekj*OappejcoLnklanpuR]hqa?khha_pekj± CapLnklanpuR]hqao$Ouopai*?kjbecqn]pekj*Oappejco?kjpatp_kjpatp(± Ouopai*?kjbecqn]pekj*OappejcoLnklanpu?khha_pekj_khha_pekj% w OappejcoLnklanpuR]hqa?khha_pekjoappejco9± jasOappejcoLnklanpuR]hqa?khha_pekj$%7 ++I]gaoqnaukqd]ra]jajpnubknpdeoqoanj]iaejpdaTIH`]p] opnejcqoanj]ia9_kjpatpWQoanJ]iaY]oopnejc7 eb$opnejc*EoJqhhKnAilpu$qoanj]ia%% w ++Cappdalnkbehar]hqaobknpdaqoan @e_pekj]nu8opnejc(k^fa_p:qoanoLnklanpeao9CapQoanLnkbeha$qoanj]ia%7 bkna]_d$OappejcoLnklanpulnklanpuej_khha_pekj% w eb$lnklanpu*LnklanpuPula*EoLneieperaxx± lnklanpu*LnklanpuPula99pulakb$Opnejc%% lnklanpu*Oane]heva=o9OappejcoOane]heva=o*Opnejc7 ahoa lnklanpu*Oane]heva=o9OappejcoOane]heva=o*Tih7 OappejcoLnklanpuR]hqaoappejc9jasOappejcoLnklanpuR]hqa$lnklanpu%7 eb$qoanoLnklanpeao9jqhh% w oappejc*Eo@enpu9b]hoa7 eb$qoanoLnklanpeao*?kjp]ejoGau$lnklanpu*J]ia%% w oappejc*Oane]heva`R]hqa9qoanoLnklanpeaoWlnklanpu*J]iaY7 oappejc*@aoane]heva`9b]hoa7 y y oappejco*=``$oappejc%7++=``pdaoappejcor]hqapkpda_khha_pekj y y napqnjoappejco7++Napqnjpdaoappejco_khha_pekj y lnkpa_pa`renpq]h@e_pekj]nu8opnejc(k^fa_p:CapQoanLnkbeha$opnejcqoanj]ia% w @e_pekj]nu8opnejc(k^fa_p:lnklanpuR]hqao9jas@e_pekj]nu8opnejc(k^fa_p:$%7 T@k_qiajptLnkbehao9T@k_qiajp*Hk]`$LnkbehaBehaL]pd%7 r]ntLnkb9$bnkilejtLnkbehao*Nkkp*Ahaiajpo$%± sdanal*=ppne^qpa$QoanJ]ia%*R]hqa*Amq]ho$qoanj]ia%± oaha_pl%7 bkna]_d$TAhaiajptihLnklanpuejtLnkb*Ahaiajpo$%% w OappejcoOane]heva=ooo9$OappejcoOane]heva=o%± Ajqi*L]noa$pulakb$OappejcoOane]heva=o%(± tihLnklanpu*=ppne^qpa$oane]heva`=o%*R]hqa%7 osep_d$oo%
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
w _]oaOappejcoOane]heva=o*>ej]nu6 lnklanpuR]hqao*=``$ tihLnklanpu*J]ia*Hk_]hJ]ia( Aj_k`ejc*=O?EE*CapOpnejc$± ?kjranp*Bnki>]oa20Opnejc$$$$T?@]p]%tihLnklanpu*BenopJk`a%*R]hqa%%%%7 ^na]g7 _]oaOappejcoOane]heva=o*Opnejc6 lnklanpuR]hqao*=``$ tihLnklanpu*J]ia*Hk_]hJ]ia( tihLnklanpu*R]hqa%7 ^na]g7 _]oaOappejcoOane]heva=o*Tih6 eb$tihLnklanpu*=ppne^qpa$pula?kjranpan%9jqhh% w Pula?kjranpan_kjranpan9$Pula?kjranpan%± =_per]pkn*?na]paEjop]j_a$± Pula*CapPula$tihLnklanpu*=ppne^qpa$pula?kjranpan%*R]hqa%%7 lnklanpuR]hqao*=``$± tihLnklanpu*J]ia*Hk_]hJ]ia(± _kjranpan*?kjranpBnkiOpnejc$tihLnklanpu*R]hqa%%7 y ^na]g7 _]oaOappejcoOane]heva=o*Lnkre`anOla_ebe_6 pdnksjasJkpOqllknpa`At_alpekj$%7 y y napqnjlnklanpuR]hqao7 y
lq^he_kranne`arke`OapLnklanpuR]hqao$± Ouopai*?kjbecqn]pekj*Oappejco?kjpatp_kjpatp(± Ouopai*?kjbecqn]pekj*OappejcoLnklanpuR]hqa?khha_pekj_khha_pekj% w opnejcqoanj]ia9_kjpatpWQoanJ]iaY]oopnejc7 ^kkhqoanEo=qpdajpe_]pa`9$^kkh%_kjpatpWEo=qpdajpe_]pa`Y7 ++Ebjkqoanj]iaeoola_ebea`(knebjklnklanpeao]napk^ao]ra`(atep eb$opnejc*EoJqhhKnAilpu$qoanj]ia%xx_khha_pekj*?kqjp99,% napqnj7 eb$Ateopo@enpuLnklanpu$_khha_pekj%% napqnj7 T@k_qiajptLnkbehao9T@k_qiajp*Hk]`$LnkbehaBehaL]pd%7 ++_da_gahaiajpo r]ntLnkb9$bnkilejtLnkbehao*Nkkp*Ahaiajpo$%± sdanal*=ppne^qpa$QoanJ]ia%*R]hqa*Amq]ho$qoanj]ia%± oaha_pl%*BenopKn@ab]qhp$%7 eb$tLnkb99jqhh% w ++=``]`ab]qhpailpulnkbeha tLnkb9jasTAhaiajp$Lnkbeha(jasT=ppne^qpa$QoanJ]ia(qoanj]ia%%7 tLnkbehao*Nkkp*=``$tLnkb%7 tLnkbehao*O]ra$LnkbehaBehaL]pd%7 y
321
322
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
++]ooqnaailpuahaiajp]osnepaaranupdejc^]_g tLnkb*NaikraJk`ao$%7 bkna]_d$OappejcoLnklanpuR]hqaoappejcej_khha_pekj% w ++Ebpdaqoaneojkp]qpdajpe_]pa`]j`pdalnklanpu`kao ++jkp]hhks]jkjuikqo]__aoo(ogeloane]hevejcep eb$qoanEo=qpdajpe_]pa`""± $^kkh%oappejc*Lnklanpu*=ppne^qpaoW=hhks=jkjuikqoY% _kjpejqa7 ++Ogelpda_qnnajplnklanpuebep#ojkp`enpu]j`eo_qnnajphu ++]ooecja`epo`ab]qhpr]hqa eb$oappejc*Eo@enpu""oappejc*Qoejc@ab]qhpR]hqa% _kjpejqa7 ++Oane]heva`]p]^]oa`kjlnklanpu#oOane]heva=opula osep_d$oappejc*Lnklanpu*Oane]heva=o% w _]oaOappejcoOane]heva=o*Opnejc6 tLnkb*=``$jasTAhaiajp$± oappejc*J]ia(± ?kjranp*PkOpnejc$oappejc*Oane]heva`R]hqa%(± jasT=ppne^qpa$oane]heva`=o(oappejc*Lnklanpu*Oane]heva=o%%%7 ^na]g7 _]oaOappejcoOane]heva=o*Tih6 ++ejopa]`kbTIHsa]ogpda`ab]qhp_kjranpan Pula?kjranpan_kjranpan9Pula@ao_nelpkn*Cap?kjranpan$± oappejc*Lnklanpu*LnklanpuPula%7 opnejc`]p]9_kjranpan*?kjranpPkOpnejc$oappejc*LnklanpuR]hqa%7 tLnkb*=``$jasTAhaiajp$± oappejc*J]ia(± `]p](± jasT=ppne^qpa$oane]heva`=o(oappejc*Lnklanpu*Oane]heva=o%(± jasT=ppne^qpa$pula?kjranpan((± _kjranpan*CapPula$%*=ooai^huMq]hebea`J]ia%%%7 ^na]g7 _]oaOappejcoOane]heva=o*>ej]nu6 ++aj_k`apda^ej]nu`]p]qoejc^]oa20aj_k`ejc opnejcaj_k`a`>ej]nu@]p]9?kjranp*Pk>]oa20Opnejc$(± oappejc*Oane]heva`R]hqa]o^upaWY%7 tLnkb*=``$jasTAhaiajp$± oappejc*J]ia(± jasT?@]p]$aj_k`a`>ej]nu@]p]%(± jasT=ppne^qpa$oane]heva`=o(oappejc*Lnklanpu*Oane]heva=o%%%7 ^na]g7 `ab]qhp6 ++qjgjksjoane]hevapula pdnksjasLnkre`anAt_alpekj$opnejc*Bkni]p$(± Ejr]he`r]hqabknOane]heva=o7atla_pa`Opnejc(Tih(kn>ej]nu((± na_aera`w,y((± Ouopai*Ajqi*CapJ]ia$oappejc*Lnklanpu*Oane]heva=o*CapPula$%((± oappejc*Lnklanpu*Oane]heva=o%%%7 y y tLnkbehao*O]ra$LnkbehaBehaL]pd%7 y
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
lnkpa_pa`renpq]hopnejcLnkbehaBehaL]pd w cap w napqnjL]pd*?ki^eja$Dppl?kjpatp*?qnnajp*Oanran*I]lL]pd$@=P=L=PD%(± Lnkbehao*tih%7 y y lnkpa_pa`renpq]h^kkhAteopo@enpuLnklanpu$± Ouopai*?kjbecqn]pekj*OappejcoLnklanpuR]hqa?khha_pekj_khha_pekj% w bkna]_d$OappejcoLnklanpuR]hqaoappejcej_khha_pekj% eb$oappejc*Eo@enpu% napqnjpnqa7 ++Ebsana]_ddana(jkja]na`enpu napqnjb]hoa7 y lq^he_kranne`aejp@ahapaEj]_peraLnkbehao$± Lnkbeha=qpdajpe_]pekjKlpekj]qpdajpe_]pekjKlpekj(± @]paPeiaqoanEj]_peraOej_a@]pa% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aejp@ahapaLnkbehao$opnejcWYqoanj]iao% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aejp@ahapaLnkbehao$LnkbehaEjbk?khha_pekjlnkbehao% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aLnkbehaEjbk?khha_pekjBej`Ej]_peraLnkbehao>uQoanJ]ia$± Lnkbeha=qpdajpe_]pekjKlpekj]qpdajpe_]pekjKlpekj(± opnejcqoanj]iaPkI]p_d(± @]paPeiaqoanEj]_peraOej_a@]pa(± ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aLnkbehaEjbk?khha_pekjBej`Lnkbehao>uQoanJ]ia$± Lnkbeha=qpdajpe_]pekjKlpekj]qpdajpe_]pekjKlpekj(± opnejcqoanj]iaPkI]p_d(ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aLnkbehaEjbk?khha_pekjCap=hhEj]_peraLnkbehao$± Lnkbeha=qpdajpe_]pekjKlpekj]qpdajpe_]pekjKlpekj(± @]paPeiaqoanEj]_peraOej_a@]pa(ejpl]caEj`at(± ejpl]caOeva(kqpejppkp]hNa_kn`o%
323
324
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aLnkbehaEjbk?khha_pekjCap=hhLnkbehao$± Lnkbeha=qpdajpe_]pekjKlpekj]qpdajpe_]pekjKlpekj(± ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y lq^he_kranne`aejpCapJqi^anKbEj]_peraLnkbehao$± Lnkbeha=qpdajpe_]pekjKlpekj]qpdajpe_]pekjKlpekj(± @]paPeiaqoanEj]_peraOej_a@]pa% w pdnksjasAt_alpekj$Pdaiapdk`knklan]pekjeojkpeilhaiajpa`*%7 y y y The class has two principal methods, CapLnklanpuR]hqao and OapLnklanpuR]hqao, for retrieving and saving Profile settings. The CapLnklanpuR]hqao method is called to obtain data from the Profile class. It then calls CapQoanLnkbehaTOREADTHESPECIFIC0ROFILEFROMTHE8-,FILE7ITHINTHE CapQoanLnkbehaMETHOD A,).1QUERYRETRIEVESTHE0ROFILE!LOOPREADSEACHVALUEANDDESERIALIZES ITDEPENDINGONITSSETTINGS"INARYDATAISSAVEDIN"ASEENCODEDFORMATANDPROTECTEDWITHINTHE 8?@=P=: section. The content node is cast to T_`]p]ANDSTRINGVALUESAREREADDIRECTLY-ORECOMPLEXTYPESARETREATEDASLnkre`anOla_ebe_, which means that the provider is responsible for finding THERIGHTSERIALIZATION4OSUPPORTTYPICAL.%4TYPESLIKEOuopai*@n]sejc*?khkn, without writing type specific code, the internal use type converter is used. This type converter usually supports the DESIGN TIMEEXPERIENCE&ORINSTANCE TOSHOWCOLORSWITHINTHELnklanpuCne` control, a type conVERTERISUSED)NTHISEXAMPLE THETYPECONVERTERSTYPEISSAVEDINTOTHEDOCUMENTANDRETRIEVED from the pula?kjranpan attribute)NTHE8-,FILE THEFULLYQUALIFIEDNAMEISSTOREDANDUSEDTO create an instance of the converter. The converter provides a ?kjranpBnkiOpnejc method, which converts from string format to the specified object. The OapLnklanpuR]hqao method reverses the whole process. First, the same LINQ statement ISUSEDTORETRIEVETHESPECIFICUSERSSECTION)FITDOESNTEXIST ANEMPTYSECTIONISADDED4HECOLlection of settings defined in the web.config file is used to assemble the current user’s Profile. Each SETTINGShTYPEvASDEFINEDINTHEweb.config) is used to obtain the associated Pula?kjranpan object, whose ?kjranpPkOpnejc methodSERIALIZESTHESETTINGSVALUE)FEVERYTHINGGOESCORRECTLY THE8-, FRAGMENTISSAVEDINTHE8-,FILE
NNote
This is a simplified scenario lacking error handling and multiuser support. It is only intended to show the construction of a custom provider and how to change the way the data is persisted.
4HE8-,WRITTENUSINGTHISCODELOOKSVERYSIMPLE ASSHOWNIN,ISTING Listing 7-26. XML That Stores User Profile Settings 8;tihranoekj9-*,aj_k`ejc9qpb)4;: 8Lnkbehao: 8LnkbehaQoanJ]ia9Qoan-: 8>]_g?khknoane]heva`=o9Lnkre`anOla_ebe_
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
pula?kjranpan9Ouopai*@n]sejc*?khkn?kjranpan(Ouopai*@n]sejc( Ranoekj9.*,*,*,(?qhpqna9jaqpn]h( Lq^he_GauPkgaj9^,/b1b3b--`1,]/]:Na`+>]_g?khkn: 8Qoan*Ai]ehoane]heva`=o9Opnejc:fkanc]_g?khknoane]heva`=o9Lnkre`anOla_ebe_ pula?kjranpan9Ouopai*@n]sejc*?khkn?kjranpan(Ouopai*@n]sejc( Ranoekj9.*,*,*,(?qhpqna9jaqpn]h( Lq^he_GauPkgaj9^,/b1b3b--`1,]/]:>hqa8+>]_g?khkn: 8Qoan*Ai]ehoane]heva`=o9Opnejc:Qoan/]_g?khkn snepa=__aooLnklanpeao9Qoan*Ai]eh(Bkna?khkn(>]_g?khkn+: 8+sa^Oanre_ao: 8+o_nelpejc: 8+ouopai*sa^*atpajoekjo: All you need to do now is write the appropriate client code.
325
326
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Creating the User Interface "ECAUSEWELLUSEPURE!*!8TOCOMMUNICATEWITHTHESERVER YOUDONTNEEDSERVER SIDECONTROLS 4HEFOLLOWINGFORMUSESBASIC(4-,TOBUILDTHE5)4HEREAREAFEW(4-,INPUTCONTROLS WITHAN )$ATTRIBUTEASSIGNEDTOEACHONESEE,ISTING Listing 7-28. Client Side Part of the Solution—the HTML Form 8bkninqj]p9oanran: 8]ol6O_nelpI]j]canE@9O_nelpI]j]cannqj]p9oanran+: 8beah`oape`9?kjp]_pBeah`oap: 8h]^ah: A)i]eh 8ejlqppula9patpe`9aI]eh+:8+h]^ah:8^n+: 8h]^ah: QoanJ]ia 8ejlqppula9patpe`9qoanJ]ia`eo]^ha`9`eo]^ha`+:8+h]^ah: 8h]^ah: 8^n+: Bkna?khkn6 8^n+: Na` 8ejlqppula9n]`ekj]ia9Bkna?khkne`9b_-r]hqa9Na`+: >hqa 8ejlqppula9n]`ekj]ia9Bkna?khkne`9b_.r]hqa9>hqa+: Cnaaj 8ejlqppula9n]`ekj]ia9Bkna?khkne`9b_/r]hqa9Cnaaj+: 8+h]^ah: 8^n+: 8h]^ah: >]_g?khkn6 8^n+: Sdepa 8ejlqppula9n]`ekj]ia9>]_g?khkne`9^_-r]hqa9Sdepa+: >aeca 8ejlqppula9n]`ekj]ia9>]_g?khkne`9^_.r]hqa9>aeca+: Uahhks 8ejlqppula9n]`ekj]ia9>]_g?khkne`9^_/r]hqa9Uahhks+: 8+h]^ah: 8^n+: 8^qppkjkj_he_g9O]raLnklanpeao$%7: O]ra8+^qppkj: 8+beah`oap: 8dn+: 8le`9Op]pqo: 8+l: 8+bkni: Figure 7-10 shows the form that appears.
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Figure 7-10. The form with simple HTML form controls
The JavaScript to Tie It Together The final step is to add the JavaScript code which will communicate between the client and the server. First, we’ll bring up the Profile for the current user and populate the form when the page LOADS4HENWELLEXPOSEAh3AVEvBUTTONSOTHATTHEUSERCANUPDATETHEIR0ROFILE4HEFINALSTEPIS to inform the user that their Profile has been saved. We’ll accomplish this with a status label that will hide itself after 5 seconds. "REAKINGTHISDOWNINTOSTEPS WELLFIRSTLOADTHE0ROFILEFORTHEAUTHENTICATEDUSERBYCALLING the Hk]` method of the Ouo*Oanre_ao*LnkbehaOanre_a object. If this call is successful, we’ll populate THEFORM)FTHEREWASANERROR WELLALERTTHEUSER4HECALLBACKMETHODSAREUSEDTOCHECKSUCCESS and error conditions. If successful, the values are read directly from the generic JavaScript class, USINGTHESAMESYNTAXFORPROPERTIESASTHESERVER3EE,ISTING Listing 7-29. JavaScript That Stores User Profile Settings sej`ks*kjhk]`9bqj_pekj$%w Ouo*Oanre_ao*LnkbehaOanre_a*hk]`$jqhh(kjHk]`Oq__aoo(kjAnnkn%7 y bqj_pekjkjHk]`Oq__aoo$k^f%w cap$aI]eh%*r]hqa9Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*Qoan*Ai]eh7 cap$qoanJ]ia%*r]hqa9Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*Qoan*J]ia7 r]nb_9Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*Bkna?khkn*J]ia7 cap$b_-%*_da_ga`9$b_99#Na`#%7 cap$b_.%*_da_ga`9$b_99#>hqa#%7 cap$b_/%*_da_ga`9$b_99#Cnaaj#%7 r]n^_9Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*>]_g?khkn*J]ia7 cap$^_-%*_da_ga`9$^_99#Sdepa#%7 cap$^_.%*_da_ga`9$^_99#>aeca#%7 cap$__/%*_da_ga`9$^_99#Uahhks#%7 y bqj_pekjkjAnnkn$annkn%w cap$Op]pqo%*ejjanDPIH9annkn*cap[iaoo]ca$%7 y The preceding code will attempt to load the Profile for the current user when the page loads. If this is successful, the kjHk]`Oq__aoo function will fire and you can populate the form. If there was an error, the kjAnnkn function will fire and an error message will be displayed to the user. 4HEFINALSTEPISTOBUILDTHEh3AVE0ROFILEvFUNCTION4HISISSIMILARTOTHEHk]` method that was previously called. Assign the new values to the Profile object in JavaScript and then call O]ra on the Ouo*Oanre_ao*LnkbehaOanre_a object!GAIN THECALLBACKMETHODSAREUSEDTOCHECKFORSUCCESS ANDERRORS ASSHOWNIN,ISTING
327
328
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Listing 7-30. Script Code That Stores User Profile Settings bqj_pekjO]raLnklanpeao$%w Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*Qoan*Ai]eh9 cap$aI]eh%*r]hqa7 Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*Qoan*J]ia9 cap$qoanj]ia%*r]hqa7 r]nb_9##7 b_'9$ cap$b_-%*_da_ga`%;#Na`#6##7 b_'9$ cap$b_.%*_da_ga`%;#>hqa#6##7 b_'9$ cap$b_/%*_da_ga`%;#Cnaaj#6##7 Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*Bkna?khkn9b_7 r]n^_9##7 ^_'9$ cap$b_-%*_da_ga`%;#Sdepa#6##7 ^_'9$ cap$b_.%*_da_ga`%;#>aeca#6##7 ^_'9$ cap$b_/%*_da_ga`%;#Uahhks#6##7 Ouo*Oanre_ao*LnkbehaOanre_a*lnklanpeao*>]_g?khkn9^_7 Ouo*Oanre_ao*LnkbehaOanre_a*o]ra$jqhh(kjO]raOq__aoo(kjAnnkn%7 y bqj_pekjkjO]raOq__aoo$%w _ha]nPeiakqp$%7 ++@eolh]updaoq__aooiaoo]capkpdaqoan* cap$Op]pqo%*ejjanDPIH9Ukqnlnkbehad]o^aajo]ra`*7 ++Naoappda`eolh]u]bpan1oa_kj`od]ral]ooa`* oapPeiakqp$bqj_pekj$%w cap$Op]pqo%*ejjanDPIH97y(1,,,%7 y )FALLGOESWELL WHENYOUCLICKTHE3AVEBUTTONYOUSHOULDSEEAMESSAGELIKETHEONEIN Figure 7-11.
Figure 7-11. The form response when the Profile access was successful 4HISISAVERYSIMPLEEXAMPLEWHICHDEMONSTRATESHOWTOACCESSTHE0ROFILEUSINGTHE!*!8 library. Its power comes from the library and its close relation to basic ASP.NET features. If you’re NOTCONFIDENTUSINGTHE!30.%4!*!8LIBRARIESBECAUSEOFTHEIRSIZEORPERFORMANCE KEEPINMIND THATTHEYSUPPORTLARGERPARTSOF!30.%4OUTOFTHEBOX4HISISVERYUSEFULFORSMALLERPROJECTS AS WELLASFORCONSTRUCTINGCOMPLEXFEATURESQUICKLY(OWEVER INLARGEPROJECTSWITHHEAVYACCESSYOU MIGHTCONSIDERDIFFERENTWAYS LIKEOTHERTHIRD PARTYLIBRARIESTHAT support the specific parts required for Profiles only.
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Extending Web Parts Personalization Providers Web Parts areSPECIALIZEDUSERCONTROLSTHATHAVEEXTENSIVECLIENTSUPPORT7EB0ARTSMAKEUPPORTIONSOFAWEBPAGE ANDALLOWUSERSTOCHANGETHEPAGEDESIGN&OREXAMPLE USERSCANMOVE7EB Parts around, hide or close them, and change their behavior. Web Parts are typically used to create PORTALPAGESTHATUSERSCANPERSONALIZE!30.%4COMESWITHALLTHEPIECESNEEDEDTOCONSTRUCT ACOMPLETELYPERSONALIZABLEPAGE4HEFRAMEWORKINCLUDESCOMPONENTSWHICHPERFORMTHEFOLLOWINGTASKS
s $EFINEANDHANDLECUSTOMIZABLESECTIONSTHROUGHZONES
s 0ULL7EB0ARTSFROMACATALOGUE
s %XPORTANDIMPORT7EB0ARTSSOTHATUSERSCANSHARETHEMLIKEGADGETS
s 3AVETHEUSERSPREFERENCESTOAPERMANENTDATASTORE
"YNOW ITSHOULDBECLEARTHATAPROVIDERPLAYSAROLEINTHIS7EB0ARTSSETTINGS LOCATIONS AND properties must be stored and retrieved when the user revisits the site. This implies that the user MUSTBEAUTHENTICATED4HEREISANINDIRECTRELATIONSHIPBETWEENTHE-EMBERSHIPAND2OLEPROVIDERS ANDTHE7EB0ARTPERSONALIZATIONPROVIDER4HEREISNODEPENDENCYONTHE0ROFILEPROVIDER AS EXPLAINEDINTHEPREVIOUSSECTION
Understanding the Web Parts Personalization Provider "EFOREYOUSTARTWORKINGWITHTHEPERSONALIZATIONPROVIDERANDPLANNINGACUSTOMIMPLEMENTATION you should understand the default provider and how it behaves. The default provider is Ouopai* Sa^*QE*Sa^?kjpnkho*Sa^L]npo*OmhLanokj]hev]pekjLnkre`an, and it accesses the same SQL Server database used to support membership, roles, and profiles internally. The prime job of the personALIZATIONPROVIDERISTOSTORETHESTATEOF7EB0ARTS WHICHCONSISTSOF7EB0ARTSCONTENTANDTHE layout of Web Parts pages. The state is held in a container of type Ouopai*Sa^*QE*Sa^?kjpnkho* Sa^L]npo*Lanokj]hev]pekjOp]pa4HEPERSONALIZATIONSERVICEUSINGTHEPROVIDERSERIALIZESTHEDATA ANDSENDSITTOTHEPROVIDERASASTREAMOFBYTESTOSTORE#ONVERSELY ITRETRIEVESANDDESERIALIZES data to restore the state of Web Parts. "ECAUSETHEOmhLanokj]hev]pekjLnkre`anISAFINALIMPLEMENTATION THEREEXISTSANABSTRACT BASECLASSFORSUCHPERSONALIZATIONPROVIDERSTHELanokj]hev]pekjLnkre`an class. lq^he_]^opn]_p_h]ooLanokj]hev]pekjLnkre`an6Lnkre`an>]oa w lnkpa_pa`Lanokj]hev]pekjLnkre`an$%7 lq^he_]^opn]_popnejc=llhe_]pekjJ]iawcap7oap7y lnkpa_pa`renpq]hEHeop?na]paOqllknpa`Qoan?]l]^ehepeao$%7 lq^he_renpq]hLanokj]hev]pekjO_kla@apaniejaEjepe]hO_kla$± Sa^L]npI]j]cansa^L]npI]j]can(Lanokj]hev]pekjOp]pahk]`a`Op]pa%7 lq^he_renpq]hE@e_pekj]nu@apaniejaQoan?]l]^ehepeao$± Sa^L]npI]j]cansa^L]npI]j]can%7 lq^he_]^opn]_pLanokj]hev]pekjOp]paEjbk?khha_pekjBej`Op]pa$± Lanokj]hev]pekjO_klao_kla(Lanokj]hev]pekjOp]paMqanumqanu(± ejpl]caEj`at(ejpl]caOeva(kqpejppkp]hNa_kn`o%7 lq^he_]^opn]_pejpCap?kqjpKbOp]pa$Lanokj]hev]pekjO_klao_kla(± Lanokj]hev]pekjOp]paMqanumqanu%7 lnkpa_pa`]^opn]_prke`Hk]`Lanokj]hev]pekj>hk^o$Sa^L]npI]j]cansa^L]npI]j]can(± opnejcl]pd(opnejcqoanJ]ia(± nab^upaWYod]na`@]p]>hk^(nab^upaWYqoan@]p]>hk^%7 lq^he_renpq]hLanokj]hev]pekjOp]paHk]`Lanokj]hev]pekjOp]pa$± Sa^L]npI]j]cansa^L]npI]j]can(^kkhecjkna?qnnajpQoan%7
329
330
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
lnkpa_pa`]^opn]_prke`NaoapLanokj]hev]pekj>hk^$± Sa^L]npI]j]cansa^L]npI]j]can(opnejcl]pd(opnejcqoanJ]ia%7 lq^he_renpq]hrke`NaoapLanokj]hev]pekjOp]pa$Sa^L]npI]j]cansa^L]npI]j]can%7 lq^he_]^opn]_pejpNaoapOp]pa$Lanokj]hev]pekjO_klao_kla(± opnejcWYl]pdo(± opnejcWYqoanj]iao%7 lq^he_]^opn]_pejpNaoapQoanOp]pa$opnejcl]pd(@]paPeiaqoanEj]_peraOej_a@]pa%7 lnkpa_pa`]^opn]_prke`O]raLanokj]hev]pekj>hk^$Sa^L]npI]j]cansa^L]npI]j]can(± opnejcl]pd(opnejcqoanJ]ia(^upaWY`]p]>hk^%7 lq^he_renpq]hrke`O]raLanokj]hev]pekjOp]pa$Lanokj]hev]pekjOp]paop]pa%7 y 4HEFOLLOWINGTABLEEXPLAINSTHEMETHODSANDPROPERTIES4HEVARIOUS]^opn]_p and renpq]h KEYWORDSINDICATETHATONLYAFEWMETHODSNEEDTOBEIMPLEMENTEDTHEOTHERMETHODSARE optional. As a result, this base class is a better starting point for a custom implementation than the Lnkre`an>]oaBASECLASSSEE4ABLE Table 7-4. Members of the PersonalizationProvider Base Class
Member
Description
=llhe_]pekjJ]ia
'ETSORSETSTHENAMEOFTHEAPPLICATION
J]ia
Name of the provider.
@ao_nelpekj
Friendly name or description.
?na]paOqllknpa`Qoan?]l]^ehepeao
2ETURNSALISTOFOuopai*Sa^*QE*Sa^?kjpnkho*Sa^L]npo* Sa^L]npQoan?]l]^ehepu objects that represent the set of KNOWNCAPABILITIES
@apaniejaEjepe]hO_kla
$ETERMINESWHETHERTHEINITIALPERSONALIZATIONSCOPESHOULD BE3HAREDOR5SERFROMLanokj]hev]pekjO_klaENUM 4AKES a Sa^L]npI]j]canOBJECTTHATMANAGESTHEPERSONALIZATION INFORMATIONANDTHEPERSONALIZATIONSTATEINFORMATION4HE method returns a Lanokj]hev]pekjO_kla enum value.
@apaniejaQoan?]l]^ehepeao
2ETURNSADICTIONARYCONTAININGSa^L]npQoan?]l]^ehepu INSTANCESTHATREPRESENTTHEPERSONALIZATION RELATEDCAPABILITIESOFTHECURRENTLYEXECUTINGUSERACCOUNT
Bej`Op]pa
2ETURNSACOLLECTIONCONTAININGZEROORMORE Lanokj]hev]pekjOp]paEjbk objects based on scope and specific query parameters. Depends on scope, query, and paging information.
Cap?kqjpKbOp]pa
2ETURNSTHENUMBEROFROWSINTHEUNDERLYINGDATASTORETHAT EXISTWITHINTHESPECIFIEDSCOPE$EPENDSONQUERYANDSCOPE
Hk]`Lanokj]hev]pekj>hk^o
,OADSRAWPERSONALIZATIONDATAFROMTHEUNDERLYINGDATA STORE4AKESAREFERENCETOTHESa^L]npI]j]can object managINGTHEPERSONALIZATIONDATA APATHASKEY THEUSERSNAME and the blob data for shared and user part.
Hk]`Lanokj]hev]pekjOp]pa
Loads the raw data from the underlying data store and converts it into a Lanokj]hev]pekjOp]pa object.
NaoapLanokj]hev]pekj>hk^
$ELETESRAWPERSONALIZATIONDATAFROMTHEUNDERLYINGDATA store.
NaoapLanokj]hev]pekjOp]pa
2ESETSPERSONALIZATIONDATATOTHEUNDERLYINGDATASTORE
NaoapOp]pa
$ELETESPERSONALIZATIONSTATEFROMTHEUNDERLYINGDATASTORE BASEDONTHESPECIFIEDPARAMETERS2ETURNSTHENUMBEROF rows deleted.
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Member
Description
NaoapQoanOp]pa
$ELETES7EB0ARTSPERSONALIZATIONDATAFROMTHEUNDERLYING data store based on the specified parameters.
O]raLanokj]hev]pekj>hk^
3AVESRAWPERSONALIZATIONDATATOTHEUNDERLYINGDATASTORE
O]raLanokj]hev]pekjOp]pa
3AVESPERSONALIZATIONDATATOADATASTORE
Implementing this is not difficult. The Hk]`, O]ra, and Naoap methods are the core parts of this class. /NECRUCIALASPECTISTHESCOPEOFTHEPERSONALIZATIONDATA7EB0ARTSPERSONALIZATIONHASTWO SCOPESBYUSERANDBYPATH4HISALLOWSYOUTOPERSONALIZETHEBEHAVIOREITHERFOREACHINDIVIDUAL USERORBYEACHPAGESPATH3COPINGBYREQUESTPATHISCALLEDhSHAREDvINTHEMETHODNAMESUSED by the provider. In these cases, the user name is set to jqhh and all settings apply to all users. The PROVIDERMUSTTAKECAREOFSUCHDATAANDSTOREITSEPARATELYFROMTHEUSERSSTORAGE The provider support is optional for multiple applications based on the =llhe_]pekjJ]ia property BUTRECOMMENDED)FYOUUSEALOCALSTORE SUCHASAN8-,FILEASSHOWNINTHEFOLLOWING EXAMPLE THEAPPLICATIONNAME can be ignored.
Implementing a Custom Personalization Provider !CUSTOMPERSONALIZATION provider has several prerequisites:
s !PORTALPAGEMUSTEXISTTHATHANDLESSEVERAL7EB0ARTS
s 4HESITEMUSTSUPPORT!UTHENTICATIONANDUSERMEMBERSHIP
s 4HECUSTOM7EB0ARTPERSONALIZATIONPROVIDERANDITSSTOREMUSTEXIST
s 4HEweb.configFILEMUSTBEPROPERLYCONFIGUREDTOSUPPORT7EB0ARTPERSONALIZATION 4HENEXTSECTIONSEXPLAINTHEREQUIRED steps one-by-one.
Creating a Portal Page /UR0ORTALPAGETOTESTTHESOLUTIONISASIMPLE7EB0ARTPAGE ASSHOWNIN,ISTING Listing 7-31. The “Portal” Page to Test the Solution 8bknie`9bkni-nqj]p9oanran: 8`er: 8]ol6Sa^L]npI]j]canE@9Sa^L]npI]j]can-nqj]p9oanran: 8Lanokj]hev]pekjAj]^ha`9pnqaLnkre`anJ]ia9TihLanokj]hev]pekjLnkre`an+: 8+]ol6Sa^L]npI]j]can: 8p]^haopuha9se`pd6-,,!: 8pnr]hecj9ie``haopuha9^]_gcnkqj`6``````: 8p`_khol]j9.: 8d.: Sah_kiapkkqnLknp]h( 8]ol6HkcejJ]iaE@9HkcejJ]ia-nqj]p9oanran+: 8+d.: 8+p`: 8p`: 8]ol6IajqE@9Iajq-nqj]p9oanran± KjIajqEpai?he_g9Iajq-[IajqEpai?he_g: 8+]ol6Iajq:
331
332
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
8+p`: 8+pn: 8pnr]hecj9pkl: 8p`opuha9se`pd6.,!: 8]ol6?]p]hkcVkjaE@9?]p]hkcVkja-nqj]p9oanran: 8VkjaPailh]pa: 8]ol6L]ca?]p]hkcL]npE@9L]ca?]p]hkcL]np-nqj]p9oanran+: 8+VkjaPailh]pa: 8+]ol6?]p]hkcVkja: 8]ol6A`epknVkjaE@9A`epknVkja-nqj]p9oanran: 8+]ol6A`epknVkja: 8+p`: 8p`opuha9se`pd62,!: 8]ol6Sa^L]npVkjaE@9Sa^L]npVkja-nqj]p9oanran: 8+]ol6Sa^L]npVkja: 8+p`: 8p`opuha9se`pd6.,!: 8]ol6Sa^L]npVkjaE@9Sa^L]npVkja.nqj]p9oanran: 8Pepha>]nRan^Opuha>]_g?khkn9=_pera>kn`an+: 8VkjaPailh]pa: 8]ol6?]haj`]nE@9?]haj`]n-nqj]p9oanran:8+]ol6?]haj`]n: 8]ol6BehaQlhk]`E@9BehaQlhk]`-nqj]p9oanran+: 8+VkjaPailh]pa: 8+]ol6Sa^L]npVkja: 8+p`: 8+pn: 8pn: 8p`_khol]j9/: 8]ol6HkcejOp]pqoE@9HkcejOp]pqo-nqj]p9oanran+: 8+p`: 8+pn: 8+p]^ha: 8+`er: 8+bkni: The only dependency on other code parts is the selection of the current provider: 8Lanokj]hev]pekjAj]^ha`9pnqaLnkre`anJ]ia9TihLanokj]hev]pekjLnkre`an+: !SSHOWN THEPERSONALIZATIONMUSTBETURNEDONINORDERTOACTIVATETHEPROVIDERUSINGTHE Aj]^ha` attribute. The name of the provider is defined in web.config3EETHESECTIONh#ONFIGURE the Provider” for more details.) 4OFORCETHEPAGETOCHANGETHESETTINGSANDINVOKETHEPROVIDER THEUSERMUSTENTERTHE%DIT mode of the Web Part page. To do this easily, the following code-behind code is used: lq^he_l]npe]h_h]oo[@ab]qhp6Ouopai*Sa^*QE*L]ca w lnkpa_pa`rke`L]ca[Hk]`$k^fa_poaj`an(Arajp=ncoa% w eb$EoLkop>]_g% w ++?na]pa]iajqkbsa^l]npik`ao IajqEpainkkpEpai9jasIajqEpai$Oaha_pSa^L]npIk`a%7 bkna]_d$Sa^L]np@eolh]uIk`aik`aejSa^L]npI]j]can-*@eolh]uIk`ao% w nkkpEpai*?deh`Epaio*=``$jasIajqEpai$ik`a*J]ia%%7 y Iajq-*Epaio*=``$nkkpEpai%7
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
y y lnkpa_pa`rke`Iajq-[IajqEpai?he_g$k^fa_poaj`an(IajqArajp=ncoa% w Sa^L]npI]j]can-*@eolh]uIk`a9Sa^L]npI]j]can-*@eolh]uIk`aoWa*Epai*PatpY7 y y The menu is filled with items from the collection of acceptable modes. In the IajqEpai?he_g event handler, the same value is used to set theCURRENTMODESEE&IGURE
Figure 7-12. The Web Part page
Prepare Authentication For a simple scenario to login and logout users, I set up Forms authentication in web.config. Furthermore, I store the users’ credentials directly in web.config and configure the Iai^anodel provider TOUSETHATDATAINSTEADOFDATAFROMTHEDEFAULTPROVIDER31,3ERVER 4HISCANBEACCOMPLISHEDBY handling the authentication event manually. The web.configSECTIONTHATDEFINESTHEUSERSLOOKSLIKE ,ISTING Listing 7-32. Define Some Users Quickly to Test the Application 8]qpdknev]pekj: 8`ajuqoano9;+: 8+]qpdknev]pekj: 8]qpdajpe_]pekjik`a9Bknio: 8bknio: 8_na`ajpe]hol]ooskn`Bkni]p9?ha]n: 8qoanj]ia9Qoan-l]ooskn`9Qoan-+: 8qoanj]ia9Qoan.l]ooskn`9Qoan.+: 8qoanj]ia9Qoan/l]ooskn`9Qoan/+: 8+_na`ajpe]ho: 8+bknio: 8+]qpdajpe_]pekj: The application then requires at least one page in order to change and use the profile settings, as well as a login page. The login page consists of a single HkcejCONTROLANDANEVENTHANDLERSEE ,ISTINGS AND
333
334
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Listing 7-33. The Login Page in Its Simplest Form 8^k`u: 8bknie`9bkni-nqj]p9oanran: 8`er: 8]ol6HkcejE@9Hkcej-nqj]p9oanrankj]qpdajpe_]pa9Hkcej-[=qpdajpe_]pa: 8+]ol6Hkcej: 8^n+: 8+`er: 8+bkni: 8+^k`u: 8+dpih:
Listing 7-34. An Event Handler Forces the Login Control to Use the Configured User Names lq^he_l]npe]h_h]ooHkcej6Ouopai*Sa^*QE*L]ca w lnkpa_pa`rke`L]ca[Hk]`$k^fa_poaj`an(Arajp=ncoa% w y lnkpa_pa`rke`Hkcej-[=qpdajpe_]pa$k^fa_poaj`an(=qpdajpe_]paArajp=ncoa% w a*=qpdajpe_]pa`9Bknio=qpdajpe_]pekj*=qpdajpe_]pa$Hkcej-*QoanJ]ia(± Hkcej-*L]ooskn`%7 y y 4HENEXTPAGEISALSOFORTESTINGPURPOSES)TCONTAINSASIMPLEFORMTOSAVENEWPROFILESETTINGS show current settings, and change accounts.
Create the Provider The following implementationUSES8-,TOSTOREDATA)TRENDERSTHEAPPLICATIONINDEPENDENTOF a SQL Server database. The implementation is as simple as possible—only the basic methods for LOADINGANDSAVINGDATAAREFULLYIMPLEMENTEDSEE,ISTING Listing 7-35. A Web Part Personalization Provider Using XML qoejcOuopai7 qoejcOuopai*?khha_pekjo*Cajane_7 qoejcOuopai*Hejm7 qoejcOuopai*Sa^7 qoejcOuopai*Sa^*QE*Sa^?kjpnkho*Sa^L]npo7 qoejcOuopai*EK7 qoejcOuopai*Sa^*Dkopejc7 qoejcOuopai*@]p]7 qoejcOuopai*Tih*Hejm7 qoejcOuopai*Tih*TL]pd7 j]iaol]_a=lnaoo*Atpajoe^ehepu*Sa^L]npLnkre`an w lq^he__h]ooTihLanokj]hev]pekjLnkre`an6Lanokj]hev]pekjLnkre`an w lner]pa_kjopopnejcOAPPEJCOJ=IA9SLOappejco*tih7 lner]pa_kjopopnejcOAPPEJCOP=C9SLOappejco7 lner]paopnejc_kjbecBeha7
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
lq^he_kranne`arke`Ejepe]heva$opnejcj]ia((± Ouopai*?khha_pekjo*Ola_e]heva`*J]iaR]hqa?khha_pekj_kjbec% w ^]oa*Ejepe]heva$j]ia(_kjbec%7 _kjbecBeha9Dppl?kjpatp*?qnnajp*Namqaop*I]lL]pd$(± L]pd*?ki^eja$z+=ll[@]p]+(OAPPEJCOJ=IA%%7 ++TIH eb$Beha*Ateopo$_kjbecBeha%% w T@k_qiajp_bc@k_9jasT@k_qiajp$ jasTAhaiajp$Sa^L]np@]p](jasT=ppne^qpa$?na]pa`((± @]paPeia*Jks*PkOdknp@]paOpnejc$%%( jasTAhaiajp$QoanO_kla%( jasTAhaiajp$Od]na`O_kla%%%7 _bc@k_*@a_h]n]pekj9jasT@a_h]n]pekj$-*,(qpb)4(pnqa%7 _bc@k_*O]ra$_kjbecBeha%7 y y ++JKPA6kjhuOd]na`)o_klalanokj]hev]pekjeohk]`a` lnkpa_pa`kranne`arke`Hk]`Lanokj]hev]pekj>hk^o$ Sa^L]npI]j]cansa^L]npI]j]can( opnejcl]pd( opnejcqoanJ]ia( nab^upaWYod]na`@]p]>hk^( nab^upaWYqoan@]p]>hk^% w T@k_qiajp_bc@k_9T@k_qiajp*Hk]`$_kjbecBeha%7 r]nnkkp9_bc@k_*Ahaiajp$Sa^L]np@]p]%7 eb$qoanJ]ia99jqhh% w k^fa_p_]_da`L]caOappejco9Dppl?kjpatp*?qnnajp*?]_daW± OAPPEJCOP=C'6'l]pdY7 eb$_]_da`L]caOappejco9jqhh% w od]na`@]p]>hk^9$^upaWY%_]_da`L]caOappejco7 y ahoa w r]nod]na`9nkkp*Ahaiajp$Od]na`O_kla% *Ahaiajpo$L]ca% *Oejcha$j9:± j*=ppne^qpa$j]ia%*R]hqa*Amq]ho$qoanJ]ia%%7 eb$od]na`99jqhh% w od]na`@]p]>hk^9jqhh7 y ahoa w od]na`@]p]>hk^9?kjranp*Bnki>]oa20Opnejc$od]na`*R]hqa%7 ++_]_daod]na`oappejco sa^L]npI]j]can*L]ca*?]_da*Ejoanp$OAPPEJCOP=C'6'l]pd(± od]na`@]p]>hk^(± jasOuopai*Sa^*?]_dejc*?]_da@alaj`aj_u$_kjbecBeha%%7 y y
335
336
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
y ahoa w r]nl]caAhaiajp9nkkp*TL]pdOaha_pAhaiajp$ Opnejc*Bkni]p$++QoanO_kla+QoanW<j]ia9#w,y#Y+L]caW<j]ia9#w-y#Y( qoanJ]ia( l]pd%%7 eb$l]caAhaiajp9jqhh% w qoan@]p]>hk^9?kjranp*Bnki>]oa20Opnejc$l]caAhaiajp*R]hqa%7 y y y lnkpa_pa`kranne`arke`NaoapLanokj]hev]pekj>hk^$ Sa^L]npI]j]cansa^L]npI]j]can( opnejcl]pd( opnejcqoanJ]ia% w y lnkpa_pa`kranne`arke`O]raLanokj]hev]pekj>hk^$ Sa^L]npI]j]cansa^L]npI]j]can( opnejcl]pd( opnejcqoanJ]ia( ^upaWY`]p]>hk^% w opnejco>hk^9?kjranp*Pk>]oa20Opnejc$`]p]>hk^%7 hk_g$pdeo% w T@k_qiajp_bc@k_9T@k_qiajp*Hk]`$_kjbecBeha%7 r]nnkkp9_bc@k_*Ahaiajp$Sa^L]np@]p]%7 eb$Opnejc*EoJqhhKnAilpu$qoanJ]ia%% w ++O_kla6qoan r]nqoanAhaiajp9nkkp*TL]pdOaha_pAhaiajp$ Opnejc*Bkni]p$++QoanO_kla+QoanW<j]ia9#w,y#Y( qoanJ]ia%%7 eb$qoanAhaiajp99jqhh% w qoanAhaiajp9nkkp*Ahaiajp$QoanO_kla%7 ++jkqoan(]``_kilhapapnaa qoanAhaiajp*=``$ jasTAhaiajp$Qoan(jasT=ppne^qpa$j]ia(qoanJ]ia%( jasTAhaiajp$L]ca(jasT=ppne^qpa$j]ia(l]pd%( o>hk^%%%7 y ahoa w ++sepdqoan(_da_gl]ca r]nl]caAhaiajp9qoanAhaiajp*Ahaiajpo$L]ca% *Oejcha$j9:± j*=ppne^qpa$j]ia%*R]hqa*Amq]ho$l]pd%%7 eb$l]caAhaiajp99jqhh% w ++jkl]ca qoanAhaiajp*=``$jasTAhaiajp$L]ca(
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
jasT=ppne^qpa$j]ia(l]pd%( o>hk^%%7 y ahoa w ++jas`]p]bknl]ca l]caAhaiajp*R]hqa9o>hk^7 y y y ahoa w ++O_kla6Od]na` r]nod]na`Ahaiajp9nkkp*Ahaiajpo$Od]na`O_kla% *Ahaiajpo$L]ca% *Oejcha$l9:± l*=ppne^qpa$J]ia%*R]hqa*Amq]ho$l]pd%%7 eb$od]na`Ahaiajp99jqhh% w od]na`Ahaiajp*=``$jasTAhaiajp$L]ca( jasT=ppne^qpa$j]ia(l]pd%( o>hk^%%7 y ahoa w od]na`Ahaiajp*R]hqa9o>hk^7 y y _bc@k_*O]ra$_kjbecBeha%7 y y lq^he_kranne`aopnejc=llhe_]pekjJ]ia w cap7 oap7 y lq^he_kranne`aejpCap?kqjpKbOp]pa$ Lanokj]hev]pekjO_klao_kla( Lanokj]hev]pekjOp]paMqanumqanu% w napqnj,7 y lq^he_kranne`aLanokj]hev]pekjOp]paEjbk?khha_pekjBej`Op]pa$ Lanokj]hev]pekjO_klao_kla( Lanokj]hev]pekjOp]paMqanumqanu( ejpl]caEj`at( ejpl]caOeva( kqpejppkp]hNa_kn`o% w pkp]hNa_kn`o9,7 napqnjjqhh7 y lq^he_kranne`aejpNaoapOp]pa$
337
338
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Lanokj]hev]pekjO_klao_kla( opnejcWYl]pdo( opnejcWYqoanj]iao% w napqnj,7 y lq^he_kranne`aejpNaoapQoanOp]pa$ opnejcl]pd( @]paPeiaqoanEj]_peraOej_a@]pa% w napqnj,7 y y y During the call to Ejepe]heva ADATASTOREINTHEFORMOFAN8-,FILEWILLBECREATED IFTHERE isn’t one already. When the user changes properties of Web Parts, the O]raLanokj]hev]pekj>hk^ method is called. The approach used is to search for the qoanJ]ia, and if it is jqhh, meaning the user ISNOTAUTHENTICATED ASSUMETHATITSASHAREDSCOPESETTING/THERWISE ITSAPERSONALSETTING AND THEUSERELEMENTISRETRIEVED)ALSOASSUMETHATDATAFORTHISUSERDOESNOTALREADYEXIST4HISISWHY 80ATHEXPRESSIONSAREUSEDINSTEADOF8,INQEXPRESSIONCHAINS )FTHE8Qoan:ELEMENTDOESNOTEXIST it’s created using the current data. The data is passed to the provider as a byte array, and stored as A"ASEENCODEDSTRINGINTHE8-,ELEMENT)FTHE8Qoan:ELEMENTEXISTS THE8L]ca: element is RETRIEVED%ACHPERSONALIZEDPAGEHASITSOWN8-,ELEMENT)FTHISELEMENTDOESNOTEXIST ITSCREATEDANDTHEENCODEDSTRINGISUSEDASITSVALUE)FITDOESEXIST THECURRENTCONTENTSAREREPLACED with the R]hqa property. A similar technique is used to retrieve the data. Every time the page refreshes, the provider is called to return the data as a byte array. If there is no data, you can safely return jqhh. While THESHAREDDATAISSTOREDINACACHE THEDATAFOREACHUSERISRETRIEVEDDIRECTLYFROMTHE8-,FILE There is plenty of scope for improving the file handling and caching, but that’s beyond the scope OFTHISCHAPTER4OLOCATETHEDATAAGAIN AN80ATHEXPRESSIONISUSED)FAVALUEISPRESENT THE Bnki>]oa20Opnejc method converts the string into a byte array. 4HISCODEPRODUCESAN8-,FILEINTHE=ll[@]p] folder in this manner: 8;tihranoekj9-*,aj_k`ejc9qpb)4;: 8Sa^L]np@]p]?na]pa`91+-2+.,,5: 8QoanO_kla: 8Qoanj]ia9Qoan-: 8L]caj]ia9z+@ab]qhp*]olt:+sAQGs=G=cE?=NgmIRJ1_/Nh^O1TVSEqRQgqR.ReM.5q`DFr^DIqR) .ReQCBu`DIqR.ReQCBu`A-d^iBjVTEB>R5b`/>pVcE>Dd>TVSFMUTF,Q/Nd`CRR_.RuB?o=?=QIV/`sM.BoVS1gUT) Et>MtTVSFMUTF,Si5qVPFi]=QKV/`sNihoVRRs^C5dV@AB@B`hUh>d_jN]^.1hISVk]Cc98+L]ca: 8+Qoan: 8Qoanj]ia9Qoan.: 8L]caj]ia9z+@ab]qhp*]olt:+sAQGs=G=cE?=NgmIRJ1_/Nh^O1TVSEqRQgqR.ReM.5q`DFr^DIqR) .ReQCBu`DIqR.ReQCBu`A-d^iBjVTEB>R5b`/>pVcE>Dd>TVSFMUTF,Q/Nd`CRR_.RuB?o=?=QIV/`sM.BoVS1gUT) Et>MtTVSFMUTF,Si5qVPBi]=QKV/`sNihoVRRs^C5dV@AB@B`hUh>d_jN]^.1hIiVk]Cc98+L]ca: 8+Qoan: 8+QoanO_kla: 8Od]na`O_kla+: 8+Sa^L]np@]p]:
C H A P T E R 7 N S E C U R I T Y A N D U S E R M A N A G E M E N T
Although its format is not impressive, this has everything you need in order to store data for any NUMBEROFUSERSANDANYNUMBEROFPERSONALIZEDPAGES4HENUMBEROFPAGESISUSUALLYLOWFOR ACUSTOMIZABLEPORTALPAGEITSONEANDTHENUMBEROFACTIVEUSERSDEPENDSONYOURAPPLICATION &ORANINTRANETWITHUPTOSEVERALHUNDREDUSERS THE8-,FILESTORAGESOLUTIONISEASYTOIMPLEMENT and sufficiently fast.
Configure the Provider (AVINGBUILTTHEPROVIDER ITNEEDSTOBECONFIGUREDFORUSE)USE&ORMS!UTHENTICATIONINTHEEXAMple and store the users’ data in web.config, as shown in the following. This is not meant to be used as production code, but it simplifies developmentBYMAKING user management very easy. 8ouopai*sa^: 8sa^L]npo: 8lanokj]hev]pekj`ab]qhpLnkre`an9TihLanokj]hev]pekjLnkre`an: 8lnkre`ano: 8]``j]ia9TihLanokj]hev]pekjLnkre`an pula9=lnaoo*Atpajoe^ehepu*Sa^L]npLnkre`an*TihLanokj]hev]pekjLnkre`an+: 8+lnkre`ano: 8+lanokj]hev]pekj: 8+sa^L]npo: 8]qpdajpe_]pekjik`a9Bknio: 8bknio: 8_na`ajpe]hol]ooskn`Bkni]p9?ha]n: 8qoanj]ia9Qoan-l]ooskn`9qoan-+: 8qoanj]ia9Qoan.l]ooskn`9qoan.+: 8qoanj]ia9Qoan/l]ooskn`9qoan/+: 8+_na`ajpe]ho: 8+bknio: 8+]qpdajpe_]pekj: 8]qpdknev]pekj: 8`ajuqoano9;+: 8+]qpdknev]pekj:
Testing the Custom Web Part Personalization Provider You can use the three prepared user accounts to test the provider. Simply launch the application ANDLOGONUSINGONEOFTHEPREVIOUSCREDENTIALS3WITCHTOh%DITvMODEWITHTHEMENU-OVETHE 7EB0ARTSAROUNDANDREARRANGETHEPAGE4HEDATAISSAVEDIMMEDIATELY,OGOUTANDLOGBACKIN ASADIFFERENTUSER#REATEANOTHERPAGELAYOUT,OGOUTAGAINANDLOGINWITHTHEACCOUNTYOUUSED FIRST4HEPAGELAYOUTFORTHEFIRSTUSERSHOULDREAPPEAR/THERTHANTHEPROVIDER THEREISNOOTHER CUSTOMCODEENABLINGTHISFEATURE4HE7EB0ART0ERSONALIZATIONINFRASTRUCTUREISALREADYAVAILABLE in ASP.NET, and Web Parts used here are oblivious to the custom provider.
NCaution The sample provider lacks error handling and testing, and does not fully implement all the features of Web Parts providers. Before using it as production code, more development is required.
339
340
C H APT ER 7 N SEC U R ITY A ND U S ER MA NA G EMENT
Summary )NTHISCHAPTER YOULOOKEDATSECURITYAND0ROFILEFEATURES4HEUSER DRIVENSECURITYMODELISCOVEREDBYTHE-EMBERSHIPAND2OLESERVICES BOTHOFWHICHUSEPROVIDERSTOACCESSADATASTORE #REATINGCUSTOMIZEDPROVIDERSSIGNIFICANTLYEXTENDSTHEBUILT INROLEANDMEMBERSHIPMODELS /NCEAUSERISAUTHENTICATED YOUCANASSOCIATEDATAWITHHISORHER0ROFILE4HE0ROFILESERVICE USESAPROVIDERTHATCANBEEXTENDEDORREWRITTENUSINGACOMPLETELYCUSTOMIZEDVERSION!0ROFILE PROVIDERCANALSOBEREACHEDBYACLIENTUSINGPURE!*!8 ENABLINGYOUTOSAVEVALUESTOANDRETRIEVE VALUESFROMTHESERVERWITHOUTINVOKINGSERVER SIDECODE "OTHEXTENSIONMODELSPROVIDEATRANSPARENTANDSIMPLEWAYOFEXTENDINGANDCUSTOMIZING BEHAVIOR WHILEALLOWINGTHEEXISTINGCONTROLSTOFUNCTIONASTHEYWOULDWITHDEFAULTPROVIDERS 4HE7EB0ART0ERSONALIZATION0ROVIDERISANINTEGRATEDTECHNIQUEWHICHSTORESSETTINGSOF7EB0ART pages for an authenticated user in a custom data store. In this chapter, you learned which base class to implement in order to change the default behavior and storage medium.
C HAPTER
8
Site Management
I
n this chapter, I’ll look at site-specific extensibility features such as the Site Map provider, which is based on the extensible provider model that we’ve already learned about. A site map defines the navigation structure of a Web site. By default, the data store is the web.sitemap XML file. Although it’s easy to change the default setting, you’ll need a custom site map provider in order to store data anywhere other than in an XML file. The navigation depends usually on the web pages on disc. If you wish to hide the internal physical structure of your site or to serve pages that do not originate from disk files, the Renpq]hL]pdLnkre`an can accomplish this. Using this you can create a virtual site structure. In this chapter, you’ll learn how to implement and modify the behavior of the OepaI]lLnkre`an and Renpq]hL]pdLnkre`an.
Site Map Providers ASP.NET features a data-driven navigation system that uses hierarchical data sources and associated controls. It’s much easier to create a site navigational system and track the current position of the user if you use the Iajq control, PnaaReas control, or OepaI]lL]pd control. As is common in ASP.NET, these controls use a provider to obtain data from a specific data source.
Internal Site Map Provider Before you start writing your own provider, let’s look at how the internal one is implemented and how it works. The TihOepaI]lLnkre`an provider reads an XML file containing the site structure as a hierarchy of OepaI]lJk`a elements. The file itself looks like this: 8oepaI]l: 8oepaI]lJk`apepha9Dkia`ao_nelpekj9Dkiaqnh9z+`ab]qhp*]olt: 8oepaI]lJk`apepha9Oanre_ao`ao_nelpekj9Oanre_aosakbban± qnh9z+Oanre_ao*]olt: 8oepaI]lJk`apepha9Pn]ejejc`ao_nelpekj9Pn]ejejc_h]ooao± qnh9z+Pn]ejejc*]olt+: 8oepaI]lJk`apepha9?kjoqhpejc`ao_nelpekj9?kjoqhpejcoanre_ao± qnh9z+?kjoqhpejc*]olt+: 8+oepaI]lJk`a: 8+oepaI]lJk`a: 8+oepaI]l: The file is named web.sitemap by default. However, you can configure the provider to accept any filename. The best way is to use the OepaI]l@]p]Okqn_a control. A page using these controls could look like this: 341
342
C H APT ER 8 N SITE MA NA G EMENT
8^k`u: 8bknie`9bkni-nqj]p9oanran: 8`er: 8d.:QoejcOepaI]lL]pd8+d.: 8]ol6OepaI]lL]pdE@9OepaI]lL]pd-Nqj]p9oanran:8+]ol6OepaI]lL]pd: 8]ol6OepaI]l@]p]Okqn_aE@9OepaI]l@]p]Okqn_a-Nqj]p9oanran+: 8d.:QoejcPnaaReas8+d.: 8]ol6PnaaReasE@9PnaaReas-Nqj]p9Oanran@]p]Okqn_aE@9OepaI]l@]p]Okqn_a-: 8+]ol6PnaaReas: 8d.:QoejcIajq8+d.: 8]ol6IajqE@9Iajq.Nqj]p9oanran@]p]Okqn_aE@9OepaI]l@]p]Okqn_a-: 8+]ol6Iajq: 8d.:Qoejc]Dknevkjp]hIajq8+d.: 8]ol6IajqE@9Iajq-Nqj]p9oanran@]p]Okqn_aE@9OepaI]l@]p]Okqn_a- Kneajp]pekj9Dknevkjp]h Op]pe_@eolh]uHaraho9.: 8+]ol6Iajq: 8+`er: 8+bkni: 8+^k`u: This web page already has a complete navigation structure. The OepaI]lL]pd control indicates your current position in the site structure via a “breadcrumb” style display. The PnaaReas and Iajq controls create a tree of all pages. The Iajq control provides additionally a JavaScript-driven drop-down menu. Both include plenty of features and assistance for creating sophisticated menus. As shown in the example, the controls can appear several times to create top level, side level, or other kinds of menu. You can even use several OepaI]l@]p]Okqn_a controls to define different entry points in the site structure or prepare submenus and partial menus.
NTip If you have not yet used these controls intensively, the following web address is one entry point into the documentation: dppl6++io`j*ie_nkokbp*_ki+aj)qo+he^n]nu+io..3114*]olt. This model has several advantages—as well as disadvantages. On the positive side, it’s easy and fast to use, and the structure of the content relates directly to the navigation. Navigation is usually structured hierarchically, and XML represents this perfectly. There are also some negatives. First, the URL is used internally as a key, which means that you cannot use the same URL twice to point to a page from different locations in the hierarchy. It’s possible to work around this—you could add a dummy querystring parameter, or use the UrlMappings defined in the web.config, but this isn’t ideal. Furthermore, the default provider reads the definition file once the application starts, and holds the whole content in memory. Large structures operating on several levels for submenus consume a lot of memory. The internal provider is defined in the web.config file like any other: 8ouopai*sa^: 8oepaI]l`ab]qhpLnkre`an9TihOepaI]lLnkre`anaj]^ha`9pnqa: 8lnkre`ano: 8]``j]ia9TihOepaI]lLnkre`an `ao_nelpekj9@ab]qhpOepaI]llnkre`an* pula9Ouopai*Sa^*TihOepaI]lLnkre`an
C H A P T E R 8 N S I T E M A N A G E M E N T
oepaI]lBeha9Sa^*oepai]l+: 8+lnkre`ano: 8+oepaI]l: 8+ouopai*sa^: The configuration, the Site Map file, and the appropriate controls are everything you need for complete site navigation.
Localization The embedded Site Map provider supports full localization. The resource provider (demonstrated in Chapter 4) is aware of the encoded resource keys and can obtain the content from a resource file. When providing different resource files for languages or cultures, you only need one Site Map definition for each different language. It is, however, not possible to have different site structures for each culture.
Security Issues The Site Map provider model does support a security concept. If you define the roles for each node, the provider will display only those nodes the user is permitted to view. Security trimming can be switched on and off. Microsoft states that using more than 150 nodes will have a serious performance impact. When using the Site Map programmatically, the provider will return jqhh if the current user has no access rights, and an exception will occur if the user is not permitted to read the root node.
Reasons to Write a Custom Site Map Provider There are several reasons why you might consider creating a custom provider:
s 9OUSUPPORTLOCALIZATIONANDNEEDADIFFERENTSITESTRUCTUREFOREACHCULTUREWITHOUTUSING several files.
s 9OUHAVEHUNDREDSOFNODESANDNEEDSECURITYTRIMMING
s 9OUHAVELIMITEDMEMORYBUTALARGESTRUCTURE
s 9OUWANTTOSUPPORTTHESAMENODESSEVERALTIMESINTHEHIERARCHY
s 9OUWANTTOALLOWUSERSTOADDREMOVENODES
s 9OUWANTTOPERSISTTHESTRUCTUREINANALTERNATIVEDATASTORE
There are two ways of customizing the provider’s behavior: either extending the default provider, or writing your own. Extending the default provider lets you keep the XML file, but allows different behavior when retrieving nodes or adding security features. Using your own provider gives you the ability to use a different data source.
NTip You can use Tih@k_qiajp or T@k_qiajp classes to retrieve and change nodes directly in the web.sitemap file in order to edit the site structure on the fly. This does not require a new provider, but extends the behavior of the site navigation universe. Consider this before writing a lot of code just to get read-write access.
343
344
C H APT ER 8 N SITE MA NA G EMENT
Writing a Custom Site Map Provider The purpose of the provider shown here is to access another data source. For web farm or web garden scenarios, or flexible access to the site structure definition, a SQL Server database is the best choice. The amount of data is relatively small, and SQL Server retains it in memory, resulting in fast access.
Prerequisites A custom provider inherits from a base class (as we’ve seen many times). For the Site Map provider, you have a choice between the Ouopai*Sa^*Op]pe_OepaI]lLnkre`an class and its abstract base class, Ouopai*Sa^*OepaI]lLnkre`an. These greatly simplify things, because they come with several basic methods already implemented. You can use the Op]pe_OepaI]lLnkre`an if
s 9OUREADNODESEITHERONCEORRARELY
s !LLINFORMATIONISCACHEDTHROUGHTHELIFETIMEOFTHEPROVIDER
The lifetime of the provider is usually the lifetime of the application. Changing the Site Map requires a restart. If any one of the following conditions is met, the OepaI]lLnkre`an type is a better choice:
s 4HEDATAISSTOREDINADATABASE
s 4HEDATACHANGESFREQUENTLYTHROUGHOUTTHEAPPLICATIONSLIFETIME
s 4HEUSERCANCHANGETHE structure and personalize the menu.
Learning About the Base Classes Before you start implementing, you’ll need to know which methods you’ll be dealing with. First, take a look at the OepaI]lLnkre`an class: lq^he_]^opn]_p_h]ooOepaI]lLnkre`an6Lnkre`an>]oa w lnkpa_pa`OepaI]lLnkre`an$%7 lq^he_renpq]hOepaI]lJk`a?qnnajpJk`awcap7y lq^he_^kkhAj]^haHk_]hev]pekjwcap7oap7y lq^he_renpq]hOepaI]lLnkre`anL]najpLnkre`anwcap7oap7y lq^he_opnejcNaokqn_aGauwcap7oap7y lq^he_renpq]hOepaI]lJk`aNkkpJk`awcap7y lq^he_renpq]hOepaI]lLnkre`anNkkpLnkre`anwcap7y lq^he_^kkhOa_qnepuPneiiejcAj]^ha`wcap7y lq^he_arajpOepaI]lNaokhraArajpD]j`hanOepaI]lNaokhra7 lnkpa_pa`renpq]hrke`=``Jk`a$OepaI]lJk`ajk`a%7 lnkpa_pa`ejpanj]hrenpq]hrke`=``Jk`a$OepaI]lJk`ajk`a(± OepaI]lJk`al]najpJk`a%7 lq^he_renpq]hOepaI]lJk`aBej`OepaI]lJk`a$Dppl?kjpatp_kjpatp%7 lq^he_]^opn]_pOepaI]lJk`aBej`OepaI]lJk`a$opnejcn]sQnh%7 lq^he_renpq]hOepaI]lJk`aBej`OepaI]lJk`aBnkiGau$opnejcgau%7 lq^he_]^opn]_pOepaI]lJk`a?khha_pekjCap?deh`Jk`ao$OepaI]lJk`ajk`a%7 lq^he_renpq]hOepaI]lJk`aCap?qnnajpJk`a=j`Dejp=j_aopknJk`ao$ejpqlHarah%7
C H A P T E R 8 N S I T E M A N A G E M E N T
lq^he_renpq]hOepaI]lJk`aCap?qnnajpJk`a=j`DejpJaecd^kndkk`Jk`ao$ejpqlHarah(± ejp`ksjHarah%7 lq^he_]^opn]_pOepaI]lJk`aCapL]najpJk`a$OepaI]lJk`ajk`a%7 lq^he_renpq]hOepaI]lJk`a± CapL]najpJk`aNah]peraPk?qnnajpJk`a=j`Dejp@ksjBnkiL]najp$± ejps]hgqlHaraho(± ejpnah]pera@alpdBnkiS]hgql%7 lq^he_renpq]hOepaI]lJk`aCapL]najpJk`aNah]peraPkJk`a=j`Dejp@ksjBnkiL]najp$± OepaI]lJk`ajk`a(ejps]hgqlHaraho(ejpnah]pera@alpdBnkiS]hgql%7 lnkpa_pa`ejpanj]h]^opn]_pOepaI]lJk`aCapNkkpJk`a?kna$%7 lnkpa_pa`op]pe_OepaI]lJk`aCapNkkpJk`a?knaBnkiLnkre`an$± OepaI]lLnkre`anlnkre`an%7 lq^he_renpq]hrke`Dejp=j_aopknJk`ao$OepaI]lJk`ajk`a(ejpqlHarah%7 lq^he_renpq]hrke`DejpJaecd^kndkk`Jk`ao$OepaI]lJk`ajk`a(ejpqlHarah(± ejp`ksjHarah%7 lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(J]iaR]hqa?khha_pekj]ppne^qpao%7 lq^he_renpq]h^kkhEo=__aooe^haPkQoan$Dppl?kjpatp_kjpatp(OepaI]lJk`ajk`a%7 lnkpa_pa`ejpanj]hrenpq]hrke`NaikraJk`a$OepaI]lJk`ajk`a%7 lnkpa_pa`OepaI]lJk`aNaokhraOepaI]lJk`a$Dppl?kjpatp_kjpatp%7 y Tables 8-1, 8-2, and 8-3 explain the methods, events, and properties, as we need to know what to implement and the intended behavior. The only unusual thing is the length of some method names, but these names clarify the methods usage. Because the provider is transparent, controls such as Iajq or PnaaReas using the abstract base class to get their data work with a custom implementation, too. First, the properties defined by the provider are shown in Table 8-1. Table 8-1. Properties Defined in SiteMapProvider
Property
Description
J]ia
The name of the provider as configured in web.config (inherited from Lnkre`an>]oa).
@ao_nelpekj
A friendly description (inherited from Lnkre`an>]oa).
?qnnajpJk`a
Reference to the current node (and therefore the related page).
Aj]^haHk_]hev]pekj
Indicates whether the provider supports localization using resources.
L]najpLnkre`an
The parent provider, if the provider supports a hierarchy of providers.
Naokqn_aGau
The root name of the resources used to localize content.
NkkpJk`a
The root node.
NkkpLnkre`an
The root provider, if a hierarchy of providers is used.
Oa_qnepuPneiiejcAj]^ha`
Indicates whether or not the provider supports security settings. If supported, the provider doesn’t return nodes that don’t match the security level.
Second, an event that the provider should support is shown in Table 8-2.
345
346
C H APT ER 8 N SITE MA NA G EMENT
Table 8-2. Events Defined in SiteMapProvider
Event
Description
OepaI]lNaokhra
Event raised when data requested from data sources is ready. In the original implementation, this is the case when the ?qnnajpJk`a property is called.
Third, the methods that build most features are shown in Table 8-3. Table 8-3. Methods Defined in SiteMapProvider
Method
Description
=``Jk`a
Adds either the root node or another node
Bej`OepaI]lJk`a
Finds a node
Bej`OepaI]lJk`aBnkiGau
Finds a node using a key
Cap?deh`Jk`ao
Gets all child nodes of the specified node
Cap?qnnajpJk`a=j`Dejp=j_aopknJk`ao
Gets the current node and pre-loads the ancestors
Cap?qnnajpJk`a=j`DejpJaecd^kndkk`Jk`ao
Gets the current node and pre-loads the neighbors
CapL]najpJk`a
Gets the parent node
CapL]najpJk`aNah]peraPk?qnnajpJk`a=j`Dejp@ksjBnki) Gets the current node’s parent node and L]najp pre-loads all ancestors for the provided number of levels CapL]najpJk`aNah]peraPkJk`a=j`Dejp@ksjBnkiL]najp
Gets the given node’s parent and pre-loads all ancestors for the provided number of levels
CapNkkpJk`a?kna
Gets the parent regardless of the provider if a hierarchy of providers is used
CapNkkpJk`a?knaBnkiLnkre`an
Gets the parent of the specified provider if a hierarchy of providers is used
DejpJaecd^kndkk`Jk`ao
Pre-loads a number of levels down or up the hierarchy from the given node
Dejp=j_aopknJk`ao
Returns ancestor nodes for the given number of levels above the current node
Eo=__aooe^haPkQoan
Checks whether the given node is accessible to the user
Ejepe]heva
Initializes the provider (inherited from base class)
NaikraJk`a
Removes a node
NaokhraOepaI]lJk`a
Fires the OepaI]lNaokhra` event
Several methods include “Hint” in their name. Use these methods to handle data in huge hierarchies, as they allow you to deal with large amounts of data and they avoid holding the whole tree in memory. Consider implementations such as SharePoint, which could create deep hierarchies of site collections, sites, pages, and subpages that might have their own navigation systems. Using the “Hint” methods, you can define the depth that is populated from the tree. This limits the data handled by the provider to an amount that makes sense for the specific action taken by the user. It also assumes that nobody wants to see the whole hierarchy at once.
C H A P T E R 8 N S I T E M A N A G E M E N T
Sitemap Nodes in Code The provider transforms the data from the data source into a hierarchy of OepaI]lJk`a objects. The base class implements three interfaces, E?hkja]^ha, EDean]n_du@]p], and EJ]rec]paQE@]p]. This indicates the true nature of the object hierarchy, as well as how to deal with the nodes (see Table 8-4). Table 8-4. Members Defined in SiteMapNode
Member
Description
Cap=hhJk`ao
Gets a read-only list of all nodes beginning from the current node.
Cap@]p]Okqn_aReas
Gets the OepaI]l@]p]Okqn_aReas object that represents the underlying data source.
CapDean]n_de_]h@]p]Okqn_aReas
Gets the OepaI]lDean]n_de_]h@]p]Okqn_aReas object that represents the underlying hierarchical data source.
Eo=__aooe^haPkQoan
Determines whether the node is accessible according to current security context.
Eo@ao_aj`]jpKb
Determines whether the current node is a descendant of the given node.
CapAtlhe_epNaokqn_aOpnejc
Gets the localized string to retrieve the explicit resource definition. Accessible in derived classes only.
CapEilhe_epNaokqn_aOpnejc
Gets the localized string to retrieve the implicit resource definition.
?deh`Jk`ao
Gets all child nodes.
L]najpJk`a
Gets the parent node.
D]o?deh`Jk`ao
Indicates whether a node has children.
Epai
Gets the current node.
L]pd
Gets the path that describes the node.
Pula
Gets the node’s type.
@ao_nelpekj
Gets the node’s description.
J]ia
Gets the node’s name.
J]rec]paQnh
Gets the node’s URL.
R]hqa
Gets the node’s title.
=ppne^qpao
Additional attributes of the node as J]iaR]hqa?khha_pekj.
Gau
Key the node uses internally to get identified.
JatpOe^hejc
The next sibling node.
LnarekqoOe^hejc
The previous sibling node.
Na]`Kjhu
Indicates whether the node is read only.
Naokqn_aGau
The resource key used to localize the parameters.
Nkhao
The assigned roles that indicate who can access the nodes.
NkkpJk`a
The root node.
Pepha
The title of the node.
Qnh
The URL the node leads to.
347
348
C H APT ER 8 N SITE MA NA G EMENT
Implementing Security The Oa_qnepuPneiiejcAj]^ha` property indicates if the nodes are limited for the current user. It can be set in web.config as shown next. The authorization settings added show role definitions that correspond to the roles attribute of the associated web.sitemap file. 8oepaI]l`ab]qhpLnkre`an9TihOepaI]lLnkre`anaj]^ha`9pnqa: 8lnkre`ano: 8]``j]ia9TihOepaI]lLnkre`an `ao_nelpekj9@ab]qhpOepaI]llnkre`an* pula9Ouopai*Sa^*TihOepaI]lLnkre`an oepaI]lBeha9Sa^*oepai]l oa_qnepuPneiiejcAj]^ha`9pnqa+: 8+lnkre`ano: 8+oepaI]l: 8hk_]pekjl]pd9z+]`iej*]olt: 8ouopai*sa^: 8]qpdknev]pekj: 8]hhksnkhao9=`iej+: 8`ajuqoano9&+: 8+]qpdknev]pekj: 8+ouopai*sa^: 8+hk_]pekj: The default implementation used in TihOepaI]lLnkre`an resolves the nkhao attributes defined in the oepaI]lJk`a elements in the web.sitemap file. If the current user is in the role, and the NkhaLnkre`an is activated, the TihOepaI]lLnkre`an limits visibility to the correct nodes. If used programmatically, the Eo=__aooe^haPkQoan property possessed by each OepaI]lJk`a instance indicates the same thing. The list of available roles is also available in the Nkhao property. A query can use either the EHeop type directly or “*” for all. The programmatic access uses a simple fallback strategy:
s )FTHEROLEWASFOUNDORTHEQUERYCONSISTSOFh v RETURNTHENODE
s )FTHEROLEWASNOTFOUND ATTEMPTTOAUTHENTICATETHE52,DEFINEDBYTHENODE
s )F7INDOWSAUTHENTICATIONISUSED ATTEMPTTOAUTHENTICATEAGAINUSINGTHECREDENTIALSOF the current user and the ACL (access control list) of the target file for the URL.
If everything fails, the provider does not return the node and assumes that the user does not have sufficient authority. Note that this is not an inheritance strategy. If a user has the right to read a specific node, it does not follow that he or she has the right to read any subsequent node. This allows different security settings for each level. However, if the right to access a specific node is denied, the strategy stops and all child nodes are blocked. This ensures that there are no isolated child nodes accessible from any places other than the sitemap hierarchy.
Implementing a SQL Server–Based Navigation SQL Server 2008 includes a new data type, Dean]n_duE`. This can be used to implement a custom database-driven Site Map provider.
Preparing the Database First, a database is required. In this example, I use the common aspnetdb.mdf database created for the role and membership provider, if default providers are used. Listing 8-1 defines an additional table named aspnet_Navigation.
C H A P T E R 8 N S I T E M A N A G E M E N T
Listing 8-1. The Table Definition Using the HierarchyId Data Type ?NA=PAP=>HA]oljap[J]rec]pekj $ OepaI]lJk`a dean]n_due` Pephar]n_d]n$-,,% @ao_nelpekj r]n_d]n$.,,% Qnhr]n_d]n$.,,% Nkhaor]n_d]n$.,,% %
JKPJQHH( JKPJQHH( JQHH( JQHH( JQHH
Let’s proceed by filling in some data, as shown in Figure 8-1.
Figure 8-1. Table with some test values in Visual Studio Professional The Dean]n_duE` column contains the node definition as a path beginning with a slash. The internal format is binary and must be transformed using the PkOpnejc method SQL provides for this type. This is what Visual Studio does when presenting the table data in edit mode.
Creating the Provider Now that we have everything, we can create the custom provider. Listing 8-2 shows the final result. You can create it in the current Web project or in a separate project. Using it in a Web project will change the configuration slightly. The big advantage of building a custom Site Map provider in its own project is to be able to reuse it with other web applications. Listing 8-2. The SqlSiteMapProvider Using the HierarchyId Data Type qoejcOuopai7 qoejcOuopai*?khha_pekjo*Cajane_7 qoejcOuopai*?khha_pekjo*Ola_e]heva`7 qoejcOuopai*?kjbecqn]pekj*Lnkre`an7 qoejcOuopai*@]p]7 qoejcOuopai*@]p]*?kiikj7 qoejcOuopai*@]p]*Omh?heajp7 qoejcOuopai*Hejm7 qoejcOuopai*Oa_qnepu*Lanieooekjo7 qoejcOuopai*Sa^7 qoejcOuopai*Sa^*?kjbecqn]pekj7 j]iaol]_a=lnaoo*Atpajoe^ehepu*OmhOepaI]l w WOmh?heajpLanieooekj$Oa_qnepu=_pekj*@ai]j`(Qjnaopne_pa`9pnqa%Y lq^he__h]ooOmhOepaI]lLnkre`an6Op]pe_OepaI]lLnkre`an w lner]paopnejc[_kjja_p7 lner]paejp[ej`atJk`a7 lner]paejp[ej`atPepha7
349
350
C H APT ER 8 N SITE MA NA G EMENT
lner]paejp[ej`atQnh7 lner]paejp[ej`at@ao_7 lner]paejp[ej`atNkhao7 lner]paejp[ej`atL]najp7 lner]paejp[ej`atNkkp7 lner]paOepaI]lJk`a[nkkp7 lner]pa@e_pekj]nu8opnejc(OepaI]lJk`a:[jk`ao9± jas@e_pekj]nu8opnejc(OepaI]lJk`a:$-2%7 lq^he_kranne`arke`Ejepe]heva$opnejcj]ia(J]iaR]hqa?khha_pekj_kjbec% w eb$_kjbec99jqhh% pdnksjas=ncqiajpJqhhAt_alpekj$_kjbec%7 eb$Opnejc*EoJqhhKnAilpu$j]ia%% j]ia9OmhOepaI]lLnkre`an7 ^]oa*Ejepe]heva$j]ia(_kjbec%7 opnejc_kjja_p9oepaI]l7 eb$Sa^?kjbecqn]pekjI]j]can*?kjja_pekjOpnejcoW_kjja_pY99jqhh% pdnksjasLnkre`anAt_alpekj$Jk_kjja_pekj%7 [_kjja_p9Sa^?kjbecqn]pekjI]j]can*?kjja_pekjOpnejcoW_kjja_pY*± ?kjja_pekjOpnejc7 eb$Opnejc*EoJqhhKnAilpu$[_kjja_p%% pdnksjasLnkre`anAt_alpekj$jk_kjja_pekjopnejc%7 y lq^he_kranne`aOepaI]lJk`a>qeh`OepaI]l$% w hk_g$pdeo% w eb$[nkkp9jqhh% napqnj[nkkp7 Omh?kjja_pekj_kjja_pekj9jasOmh?kjja_pekj$[_kjja_p%7 pnu w _kjja_pekj*Klaj$%7 Omh?kii]j`_kii]j`7 _kii]j`9jasOmh?kii]j`$ qeh`OepaI]l$%7 napqnj[nkkp7 y lner]paOepaI]lJk`a?na]paOepaI]lJk`a$@^@]p]Na]`anna]`an(^kkhbknNkkp(± kqpopnejcl]najpGau% w opnejcjk`aOpnejc9na]`an*CapOpnejc$bknNkkp;[ej`atNkkp6[ej`atJk`a%7 opnejcpepha9na]`an*Eo@>Jqhh$[ej`atPepha%;jqhh6± na]`an*CapOpnejc$[ej`atPepha%*Pnei$%7 opnejcqnh9na]`an*Eo@>Jqhh$[ej`atQnh%;jqhh6± na]`an*CapOpnejc$[ej`atQnh%*Pnei$%7 opnejc`ao_nelpekj9na]`an*Eo@>Jqhh$[ej`at@ao_%;jqhh6± na]`an*CapOpnejc$[ej`at@ao_%*Pnei$%7 opnejcnkhao9na]`an*Eo@>Jqhh$[ej`atNkhao%;jqhh6± na]`an*CapOpnejc$[ej`atNkhao%*Pnei$%7 opnejcWYnkhaheop9jqhh7 eb$Opnejc*EoJqhhKnAilpu$nkhao%% nkhaheop9nkhao*Olhep$jas_d]nWYw#(#(#7#y(1-.%7 OepaI]lJk`ajk`a9jasOepaI]lJk`a$pdeo(jk`aOpnejc( qnh( pepha( `ao_nelpekj( nkhaheop(jqhh(jqhh(jqhh%7 l]najpGau9na]`an*Eo@>Jqhh$[ej`atL]najp%;jqhh6± na]`an*CapOpnejc$[ej`atL]najp%7 [jk`ao*=``$jk`aOpnejc(jk`a%7 napqnjjk`a7 y lner]paOepaI]lJk`aCapL]najpOepaI]lJk`a$opnejcl]najpGau% w r]nl]najp9bnkijej[jk`ao± sdanaj*Gau*Amq]ho$l]najpGau%± oaha_pj*R]hqa7 napqnjl]najp*BenopKn@ab]qhp8OepaI]lJk`a:$%7 y y y
351
352
C H APT ER 8 N SITE MA NA G EMENT
The implementation is intentionally brief. The Dean]n_duE` datatype makes it much easier to fetch a hierarchy. The heart is this SQL query, which retrieves all the needed data in one query: OAHA?P&(OepaI]lJk`a*PkOpnejc$%=OOepaI]lJk`aOpnejc( OepaI]lJk`a*Cap=j_aopkn$-%*PkOpnejc$%=OL]najp( dean]n_due`66CapNkkp$%*PkOpnejc$%=ONkkp BNKI]oljap[J]rec]pekj A great attribute of SQL Server is the ability to provide additional methods for data types. Here I use the Cap=j_aopkn and CapNkkp methods. Cap=j_aopkn$-% fetches the parent element, while CapNkkp$% obtains the hierarchy’s root. Bear in mind that some methods of these T-SQL functions are members, such as Cap=j_aopkn, while other methods are static, like CapNkkp. The double colon, “::” indicates access to static methods in SQL Server syntax. A @]p]Na]`an object provides fast forward-only access to the SQL query results, from which we assemble OepaI]lJk`a objects. The node type is provided so that the same method can be reused. The value is retrieved using na]`an*CapOpnejc$8ej`at:%. The index is the number of the column returned by the SQL query. The member fields’ [ej`atNkkp, [ej`atJk`a, or [ej`atL]najp contain the ordinal number for the column. In the SQL statement, the result is cast using PkOpnejc. This requires the CapOpnejc method when returning results. The =``Jk`a method from the Op]pe_OepaI]lLnkre`an base class is responsible for building the hierarchy. To obtain the necessary information, provide a parent for each node. In the example, I assume that the parent is already defined as a node. This requires the table data to be ordered so that parents appear before their children. The CapL]najpOepaI]lJk`a method is used to retrieve the parent from the private [jk`ao collection when building the map. This collection exists for that purpose and is no longer used when the nodes are retrieved from the sitemap controls. The l]najpGau used here is simply the value returned from SQL with the Cap=j_aopkn method, as mentioned before.
Configuring the Custom Provider The configuration consists of two parts. The first step is to define the connection string in order to access the data source. 8_kjja_pekjOpnejco: 8]``_kjja_pekjOpnejc9@]p]Okqn_a9*XOMHATLNAOO7Ejepe]h± ?]p]hkc9]oljap`^7Ejpacn]pa`Oa_qnepu9Pnqa± j]ia9oepaI]l+: 8+_kjja_pekjOpnejco: The provider itself needs the basic format, as shown before: 8ouopai*sa^: 8oepaI]laj]^ha`9pnqa`ab]qhpLnkre`an9IuOmhOepaI]lLnkre`an: 8lnkre`ano: 8]``j]ia9IuOmhOepaI]lLnkre`an pula9=lnaoo*Atpajoe^ehepu*OmhOepaI]l*OmhOepaI]lLnkre`an `ao_nelpekj9IuOmhOepaI]lLnkre`an oa_qnepuPneiiejcAj]^ha`9b]hoa _kjja_pekjOpnejcJ]ia9oepaI]l +: 8+lnkre`ano: 8+oepaI]l: 8+ouopai*sa^:
C H A P T E R 8 N S I T E M A N A G E M E N T
Finally, the data source used on the web pages needs to point to the provider. If you have several providers, each data source can use its own provider. 8]ol6OepaI]l@]p]Okqn_aE@9OepaI]l@]p]Okqn_a-nqj]p9oanran± OepaI]lLnkre`an9IuOmhOepaI]lLnkre`an+: Although there are no changes to the various controls using this data source, some features, such as security trimming, are not yet implemented. However, you’ve learned how to extend the default provider model regarding site maps, and how to benefit from the new features SQL Server 2008 provides.
Testing the Provider To test the provider, write a simple page that contains at least a Iajq and a OepaI]lL]pd control bound to the modified OepaI]l@]p]Okqn_a control. A page such as this one will suffice: 8bknie`9bkni-nqj]p9oanran: 8`er: 8]ol6OepaI]lL]pdE@9OepaI]lL]pd-nqj]p9oanran OepaI]lLnkre`an9IuOmhOepaI]lLnkre`an: 8+]ol6OepaI]lL]pd: 8^n+: 8^n+: Iajq68^n+: 8]ol6IajqE@9Iajq-nqj]p9oanran± Op]pe_@eolh]uHaraho9-@]p]Okqn_aE@9OepaI]l@]p]Okqn_a-: 8+]ol6Iajq: 8^n+: 8^n+: 8+`er: 8]ol6OepaI]l@]p]Okqn_aE@9OepaI]l@]p]Okqn_a-nqj]p9oanran OepaI]lLnkre`an9IuOmhOepaI]lLnkre`an+: 8+bkni: The data source is only required for the Iajq control. Breadcrumbs are displayed using a OepaI]lL]pd control, which can connect directly to the custom provider using its name. Figure 8-2 shows the output.
Figure 8-2. Menu and SiteMapPath controls with data from SQL Server Remember that you need to add the page names to the table data. Otherwise, you’ll encounter page load errors when you choose the menu items.
353
354
C H APT ER 8 N SITE MA NA G EMENT
Suggestions for Extending the Example The example is as simple as possible, containing nothing more than the code required to get it working. It lacks error checking and proper exception handling. Consider making the following changes before using the code in a production environment:
s !LLOWDUPLICATENODES
s $ISPOSEOFTHEDICTIONARYTHATSTORESTHEPARENTS
s /RDERTHE31,QUERYTOACCEPTUNORDEREDNODEDEFINITIONS
s !DDAPRIMARYKEYTOTHETABLEINORDERTOIMPROVESPEEDFORLARGENODESETS There are further ways of extending the behavior.
Extending the VirtualPathProvider Complex sites with hundreds of pages are difficult to maintain. For some sections, a file structure with static data seems to be more productive, whereas other sections are composed dynamically from databases. This difference should be indiscernible to regular users and search engines alike. Search engines follow each link on a page and find the navigation paths through a site, indexing the content of each page on the way. Users bookmark pages and return directly to them later. However, neither of these behaviors are what developers are looking for in creating pages. The Renpq]hL]pdLnkre`an is designed to separate the internal and external structures. Like any other provider, it works transparently, using a pluggable approach. The difference is that the virtual path provider does not access a database and internally is different from all providers described so far. However, to implement a virtual path provider, you’ll have to inherit and implement an abstract base class—Renpq]hL]pdLnkre`an.
Using the VirtualPathProvider In Chapters 4 to 7, I started with a brief overview of the default provider before looking at a custom implementation. Here it’s a bit different. There is no default provider, which means that each page is handled at its physical location and dynamically-generated content appears as is, within the one and only page possessing that functionality. Implementing a virtual path provider is all about changing the default behavior of the path resolution for any regular Web site. Some examples of what it can accomplish might help you decide if it’s worth the effort:
s 3TORINGALLCONTENTINADATABASE
s )NTERCEPTINGTHEPARSINGOFPAGESINSCENARIOSWHEREPAGESCOMEFROMDIFFERENTSOURCES
s #USTOMIZINGTHEPATHRESOLUTIONFORTHEROOTOFTHEAPPLICATIONUSINGh^v
s -ODIFYINGTHECOMPILATIONPROCESS
The parsing aspect especially is not one that you’ll be using on a daily basis. You might have heard about large ASP.NET-based projects such as SharePoint. SharePoint is an application providing a content management system, among several other features. Users can create content and add active parts—called webparts—to these pages. The content is stored in a database and pages are built dynamically. ASP.NET does not support this out-of-the-box, as the compiler would not be able to locate any pages to compile. A powerful virtual path provider is used to resolve pages found in the database, mix them with those still stored as files, and create a final structure.
C H A P T E R 8 N S I T E M A N A G E M E N T
While SharePoint is a technically admirable example of a virtual path provider, in our example we’ll consider a more modest scenario. Given a Web site with a “MySite” section, let’s expose it with a structure like this: Oepa IuOepaFkanc ?kjpnkho IuOepaFkdj ?kjpnkho IuOepaG]pdnuj ?kjpnkho Users will think that they have a private site. They can upload and modify pages there, and you could even allow them to store files. However, if users follow convention and use paths such as ~/controls/mycontrol.ascx to link their web pages to user controls, it will fail. Consider this example: 8!qeh`I]j]can*EoLna_kileha`=ll% w NaceopanRenpq]hL]pdLnkre`anEjpanj]h$renpq]hL]pdLnkre`an%7 y y The =olJapDkopejcLanieooekj attribute means that you’ll need to run in full trust in order to register a provider. If you don’t have permission, a Oa_qnepuAt_alpekj is thrown. This is the same reason for the check regarding pre-compilation. Both tests ensure that a Renpq]hL]pdLnkre`an cannot be used without permissions or within a running environment. The third check searches for an existing hosting environment. Without this, a path provider cannot function. While the missing hosting environment throws an exception, the pre-compilation check allows the method to fail silently. To understand why, let’s examine the Ejepe]heva method in NaceopanRenpq]hL]pdLnkre`anEjpanj]h. It’s not the public method, but the internal one that looks like this: ejpanj]hrenpq]hrke`Ejepe]heva$Renpq]hL]pdLnkre`anlnarekqo% w pdeo*[lnarekqo9lnarekqo7 pdeo*Ejepe]heva$%7 y If a Renpq]hL]pdLnkre`an supports a fallback scenario, this setting ensures that the “fallback provider” is available. The overridden Ejepe]heva method that you may have changed is called after this.
C H A P T E R 8 N S I T E M A N A G E M E N T
Helpful Classes for Path and File Operations DkopejcAjrenkjiajp is a class defined in Ouopai*Sa^*Dkopejc that defines the very basic types used to support the ASP.NET hosting environment. The same namespace contains the Renpq]hL]pdLnkre`an base class. The DkopejcAjrenkjiajp class contains several static methods. Some are useful when dealing with paths and path resolving issues.
The HostingEnvironment Class Let’s take a look at the path handling capabilities provided by ASP.NET before we start writing a path provider. Table 8-5 shows a selection of methods in the class, which explicitly support path operations. Table 8-5. Members of the HostingEnvironment Class Pertaining to Path Operations
Method or Property
Description
I]lL]pd
Maps a virtual path to its physical counterpart on the server
NaceopanRenpq]hL]pdLnkre`an
Registers a virtual path provider
=llhe_]pekjLduoe_]hL]pd
Gets the physical path of the application
=llhe_]pekjRenpq]hL]pd
Gets the virtual path of the application
Renpq]hL]pdLnkre`an
Gets the currently registered path provider
OepaJ]ia
The name of the site
All properties and methods mentioned here are static and don’t require an instance of the class. The other methods and properties concern application domains, object registering, caching, and impersonation. However, that’s beyond the scope of this chapter. If you’d like to learn more, take a look at the following MSDN page: dppl6++io`j*ie_nkokbp*_ki+aj)qo+he^n]nu+ouopai*sa^*dkopejc*dkopejcajrenkjiajp*]olt
VirtualFile and VirtualDirectory Renpq]hBeha allows direct access to virtual files and reads the file contents as a stream: lq^he_]^opn]_p_h]ooRenpq]hBeha6Renpq]hBeha>]oa w lnkpa_pa`Renpq]hBeha$opnejcrenpq]hL]pd%7 lq^he_kranne`a^kkhEo@ena_pknuwcap7y lq^he_]^opn]_pOpna]iKlaj$%7 y Eo@ena_pknu always returns b]hoa. The Klaj method must be overridden and return a read-only stream to the virtual resource. The Renpq]h@ena_pknu class is similar and manages a directory: lq^he_]^opn]_p_h]ooRenpq]h@ena_pknu6Renpq]hBeha>]oa w lnkpa_pa`Renpq]h@ena_pknu$opnejcrenpq]hL]pd%7 lq^he_]^opn]_pEAjqian]^ha?deh`najwcap7y lq^he_]^opn]_pEAjqian]^ha@ena_pkneaowcap7y lq^he_]^opn]_pEAjqian]^haBehaowcap7y lq^he_kranne`a^kkhEo@ena_pknuwcap7y y
357
358
C H APT ER 8 N SITE MA NA G EMENT
@ena_pkneao lists the subdirectories in this virtual directory. Behao returns the files only. ?deh`naj returns both the files and the folders. Eo@ena_pknu always returns pnqa.
VirtualPathUtility This class is defined in the Ouopai*Sa^ namespace and provides further useful methods (see Table 8-6). Table 8-6. Members of the VirtualPathUtility Class
Method or Property
Description
=llaj`Pn]ehejcOh]od
Adds a slash at the end of the path if there isn’t one
?ki^eja
Concatenates a base path and a relative path
Cap@ena_pknu
Gets the directory path of a virtual path
CapAtpajoekj
Gets the extension of a file with a virtual path
CapBehaJ]ia
Gets the file name of a file with a virtual path
Eo=^okhqpa
Indicates whether a path is absolute
Eo=llNah]pera
Indicates whether a virtual path is relative against the application
I]gaNah]pera
4RANSFORMSANAPPLICATIONRELATIVEPATHWITHSTEMOPERATORh^vINTO a relative virtual path
NaikraPn]ehejcOh]od
Removes a trailing slash from the end; does nothing if there is no slash present
Pk=^okhqpa
Converts a virtual path into an absolute one
Pk=llNah]pera
Converts an absolute path into a relative one
All of these methods are static, and thus don’t require an instance of the class to be created.
Creating a Virtual Path Provider to Get Themes from Database Now that we have all the prerequisites, let’s write a provider. In this example, I want to change the behavior of the =ll[Pdaiao folder. A theme is usually a static conglomeration of skins, style sheet files, and images. You can specify a theme either in web.config or within each web page. Let’s define individual themes in another folder and relate them to pages. This is possible using a custom Renpq]hL]pdLnkre`an. Its path is defined in web.config, and you can either set it dynamically or manipulate it. The provider shown in this section gives you the basic framework for handling file access and file resolving.
Prepare the Project The themes are still in a folder in your project. The database only defines a reference to the theme’s name. To avoid confusing Visual Studio with a regular theme, all themes are kept in a different folder named Path_Themes. You can use any valid name except for =ll[Pdaiao, which is reserved.
Create the VirtualPathProvider The path provider should function as a transparent layer for ASP.NET. Implementing it completely requires more steps than other providers. For the sake of brevity, I’ve created a simplified but functional path provider that consists of three classes:
C H A P T E R 8 N S I T E M A N A G E M E N T
s Renpq]hPdaiaBeha
s Renpq]hPdaia@ena_pknu
s PdaiaL]pdLnkre`an
This provider lacks caching, which causes it to work slowly, but it’s fully functional. However, the cache implementation is not particularly difficult and can be added easily. I’ve excluded it, as it’s out of this section’s scope. The Renpq]hPdaiaBeha class holds a reference to a specific file from the Path_Themes folder. This is usually a skin file, a style sheet, or a resource (such as an image) used with a theme. The file’s complete location is stored in the =^okhqpaL]pd property and used in the Klaj method. (The Klaj method is called to read the file’s contents.) The L]najp property points to the containing directory (see Listing 8-3). Listing 8-3. Handling Virtual Files lq^he__h]ooRenpq]hPdaiaBeha6Ouopai*Sa^*Dkopejc*Renpq]hBeha w lner]paopnejc[pdaia=^okhqpaL]pd9Opnejc*Ailpu7 lner]paopnejc[chk^]h=^okhqpaL]pd9Opnejc*Ailpu7 lner]paRenpq]hPdaia@ena_pknu[l]najp9jqhh7 lq^he_Renpq]hPdaiaBeha$Opnejcrenpq]hL]pd( opnejcpdaia=^okhqpaL]pd( opnejcchk^]h=^okhqpaL]pd( Renpq]hPdaia@ena_pknul]najp% 6^]oa$renpq]hL]pd% w [pdaia=^okhqpaL]pd9pdaia=^okhqpaL]pd7 [chk^]h=^okhqpaL]pd9chk^]h=^okhqpaL]pd7 [l]najp9l]najp7 y lq^he_kranne`aOpna]iKlaj$% w napqnjBeha*Klaj$=^okhqpaL]pd(BehaIk`a*Klaj%7 y lq^he_Renpq]hPdaia@ena_pknuL]najp w cap w napqnj[l]najp7 y y lner]paOpnejc=^okhqpaL]pd w cap w ++Cappda_qnnajpoapr]hqa Opnejc_qnnajpOap9PdaiaL]pdLnkre`an*?qnnajp*?qnnajpOap7 eb$$Opnejc*EoJqhhKnAilpu$[pdaia=^okhqpaL]pd%% ""$L]najp*BehaEoEj_hq`a`$J]ia(_qnnajpOap(pnqa%%% napqnj[pdaia=^okhqpaL]pd7
359
360
C H APT ER 8 N SITE MA NA G EMENT
ahoaeb$$Opnejc*EoJqhhKnAilpu$[chk^]h=^okhqpaL]pd%% ""$L]najp*BehaEoEj_hq`a`$J]ia(_qnnajpOap(b]hoa%%% napqnj[chk^]h=^okhqpaL]pd7 napqnjOpnejc*Ailpu7 y y ejpanj]h>kkha]jAteopoEjPdaia@ena_pknu w cap w napqnj$Opnejc*EoJqhhKnAilpu$[pdaia=^okhqpaL]pd%%7 y y ejpanj]h>kkha]jAteopoEjChk^]h@ena_pknu w cap w napqnj$Opnejc*EoJqhhKnAilpu$[chk^]h=^okhqpaL]pd%%7 y y y The main focus of the implementation is the directory handling. Hence, the Renpq]hPdaia@ena_pknu class contains much more code (see Listing 8-4). Listing 8-4. Handling Virtual Directories lq^he__h]ooRenpq]hPdaia@ena_pknu6Renpq]h@ena_pknu w opnq_pEpaiOa]n_dEjbk w lq^he_OpnejcJ]ia7 lq^he_OpnejcRenpq]hL]pd7 lq^he_OpnejcPdaia=^okhqpaL]pd7 lq^he_OpnejcChk^]h=^okhqpaL]pd7 y lner]paRenpq]hPdaia@ena_pknu[l]najp9jqhh7 lner]paOpnejc[pdaia=^okhqpaL]pd9Opnejc*Ailpu7 lner]paOpnejc[chk^]h=^okhqpaL]pd9Opnejc*Ailpu7 lner]pa@e_pekj]nu8Opnejc(Renpq]hPdaia@ena_pknu:[`ena_pkneao9jqhh7 lner]pa@e_pekj]nu8Opnejc(Renpq]hPdaiaBeha:[behao9jqhh7 lner]pa@e_pekj]nu8Opnejc(Renpq]hBeha>]oa:[_deh`naj9jqhh7 lq^he_Renpq]hPdaia@ena_pknu$Opnejcrenpq]hL]pd% 6pdeo$renpq]hL]pd(Opnejc*Ailpu(Opnejc*Ailpu(jqhh% w y lq^he_Renpq]hPdaia@ena_pknu$Opnejcrenpq]hL]pd( Opnejcpdaia=^okhqpaL]pd( Opnejcchk^]h=^okhqpaL]pd% 6pdeo$renpq]hL]pd(pdaia=^okhqpaL]pd(chk^]h=^okhqpaL]pd(jqhh% w
C H A P T E R 8 N S I T E M A N A G E M E N T
y lq^he_Renpq]hPdaia@ena_pknu$Opnejcrenpq]hL]pd( Opnejcpdaia=^okhqpaL]pd( Opnejcchk^]h=^okhqpaL]pd( Renpq]hPdaia@ena_pknul]najp% 6^]oa$renpq]hL]pd% w eb$Opnejc*EoJqhhKnAilpu$pdaia=^okhqpaL]pd%% w OpnejcoPdaiaNah]peraL]pd9± PdaiaL]pdLnkre`an*?qnnajp*?kjranpPkPdaiaNah]peraL]pd$renpq]hL]pd%7 pdaia=^okhqpaL]pd9± Dppl?kjpatp*?qnnajp*Oanran*I]lL]pd$oPdaiaNah]peraL]pd%7 y eb$@ena_pknu*Ateopo$pdaia=^okhqpaL]pd%% w pdaia=^okhqpaL]pd9Opnejc*Ailpu7 y eb$Opnejc*EoJqhhKnAilpu$chk^]h=^okhqpaL]pd%% w OpnejcoChk^]hNah]peraL]pd9± PdaiaL]pdLnkre`an*?qnnajp*?kjranpPkChk^]hNah]peraL]pd$Renpq]hL]pd%7 chk^]h=^okhqpaL]pd9± Dppl?kjpatp*?qnnajp*Oanran*I]lL]pd$oChk^]hNah]peraL]pd%7 y eb$@ena_pknu*Ateopo$chk^]h=^okhqpaL]pd%% chk^]h=^okhqpaL]pd9Opnejc*Ailpu7 [pdaia=^okhqpaL]pd9pdaia=^okhqpaL]pd7 [chk^]h=^okhqpaL]pd9chk^]h=^okhqpaL]pd7 [l]najp9l]najp7 ++?na]papda_khha_pekjopkdkh`pdarenpq]hepaio [behao9jas@e_pekj]nu8opnejc(Renpq]hPdaiaBeha:$%7 [`ena_pkneao9jas@e_pekj]nu8opnejc(Renpq]hPdaia@ena_pknu:$%7 [_deh`naj9jas@e_pekj]nu8opnejc(Renpq]hBeha>]oa:$%7 Bej`Behao$%7 Bej`Oq^@ena_pkneao$%7 Bej`?deh`naj$%7 y lner]parke`Bej`Behao$% w @e_pekj]nu8Opnejc(EpaiOa]n_dEjbk:behaHeop9± jas@e_pekj]nu8opnejc(EpaiOa]n_dEjbk:$%7 eb$@ena_pknu*Ateopo$Pdaia=^okhqpaL]pd%% w r]nbehao9bnkibej@ena_pknu*CapBehao$Pdaia=^okhqpaL]pd% oaha_pjasEpaiOa]n_dEjbk w J]ia9L]pd*CapBehaJ]ia$b%( Renpq]hL]pd9Renpq]hL]pdQpehepu*?ki^eja$Renpq]hL]pd(±
361
362
C H APT ER 8 N SITE MA NA G EMENT
L]pd*CapBehaJ]ia$b%%( Pdaia=^okhqpaL]pd9b y7 bkna]_d$EpaiOa]n_dEjbkbehaEjbkejbehao% w behaHeop*=``$behaEjbk*J]ia(behaEjbk%7 y y eb$@ena_pknu*Ateopo$Chk^]h=^okhqpaL]pd%% w r]nbehao9bnkibej@ena_pknu*CapBehao$Chk^]h=^okhqpaL]pd% oaha_pjasEpaiOa]n_dEjbk w J]ia9L]pd*CapBehaJ]ia$b%( Renpq]hL]pd9Renpq]hL]pdQpehepu*?ki^eja$Renpq]hL]pd(± L]pd*CapBehaJ]ia$b%%( Chk^]h=^okhqpaL]pd9b y7 bkna]_d$EpaiOa]n_dEjbkbehaEjbkejbehao% w eb$behaHeop*?kjp]ejoGau$behaEjbk*J]ia%% w EpaiOa]n_dEjbkpdaiaBehaEjbk9behaHeopWbehaEjbk*J]iaY7 behaHeop*Naikra$pdaiaBehaEjbk*J]ia%7 behaHeop*=``$pdaiaBehaEjbk*J]ia(pdaiaBehaEjbk%7 y ahoa w behaHeop*=``$behaEjbk*J]ia(behaEjbk%7 y y y ++Hkklpdnkqcda]_dbehabkqj` bkna]_d$EpaiOa]n_dEjbkbehaEjbkejbehaHeop*R]hqao% w ++=``a]_dbehapkpdabehao`e_pekj]nuqoejcpda ++ejbkni]pekjopkna`bknpdabeha [behao*=``$behaEjbk*J]ia(jasRenpq]hPdaiaBeha$behaEjbk*Renpq]hL]pd(± behaEjbk*Pdaia=^okhqpaL]pd(± behaEjbk*Chk^]h=^okhqpaL]pd(± pdeo%%7 y y lner]parke`Bej`Oq^@ena_pkneao$% w @e_pekj]nu8Opnejc(EpaiOa]n_dEjbk:`ena_pknuHeop9± jas@e_pekj]nu8opnejc(EpaiOa]n_dEjbk:$%7 Bqj_8opnejc(opnejc(opnejc:I]gaL]pd9`ahac]pa$opnejc^(opnejcr% w napqnjRenpq]hL]pdQpehepu*=llaj`Pn]ehejcOh]od$± Renpq]hL]pdQpehepu*?ki^eja$^(r%%7 y7 eb$@ena_pknu*Ateopo$Pdaia=^okhqpaL]pd%%
C H A P T E R 8 N S I T E M A N A G E M E N T
w r]npdaia@ena_pkneao9± bnkipej@ena_pknu*Cap@ena_pkneao$Pdaia=^okhqpaL]pd% oaha_pjasEpaiOa]n_dEjbk w J]ia9L]pd*CapBehaJ]ia$p%( Renpq]hL]pd9I]gaL]pd$Renpq]hL]pd(± L]pd*CapBehaJ]ia$p%%( Pdaia=^okhqpaL]pd9p y7 bkna]_d$EpaiOa]n_dEjbk`ena_pknuEjbkejpdaia@ena_pkneao% w `ena_pknuHeop*=``$`ena_pknuEjbk*J]ia(`ena_pknuEjbk%7 y y eb$@ena_pknu*Ateopo$Chk^]h=^okhqpaL]pd%% w r]npdaia@ena_pkneao9± bnkipej@ena_pknu*Cap@ena_pkneao$Chk^]h=^okhqpaL]pd% oaha_pjasEpaiOa]n_dEjbk w J]ia9L]pd*CapBehaJ]ia$p%( Renpq]hL]pd9I]gaL]pd$Renpq]hL]pd(± L]pd*CapBehaJ]ia$p%%( Chk^]h=^okhqpaL]pd9p y7 bkna]_d$EpaiOa]n_dEjbk`ena_pknuEjbkejpdaia@ena_pkneao% w eb$`ena_pknuHeop*?kjp]ejoGau$`ena_pknuEjbk*J]ia%% w EpaiOa]n_dEjbkpdaia@ena_pknuEjbk9± `ena_pknuHeopW`ena_pknuEjbk*J]iaY7 `ena_pknuHeop*Naikra$pdaia@ena_pknuEjbk*J]ia%7 `ena_pknuHeop*=``$pdaia@ena_pknuEjbk*J]ia(pdaia@ena_pknuEjbk%7 y ahoa w `ena_pknuHeop*=``$`ena_pknuEjbk*J]ia(`ena_pknuEjbk%7 y y y bkna]_d$EpaiOa]n_dEjbk`ena_pknuEjbkej`ena_pknuHeop*R]hqao% w Renpq]hPdaia@ena_pknu`ena_pknu9jasRenpq]hPdaia@ena_pknu$± `ena_pknuEjbk*Renpq]hL]pd(± `ena_pknuEjbk*Pdaia=^okhqpaL]pd(± `ena_pknuEjbk*Chk^]h=^okhqpaL]pd(± pdeo%7 [`ena_pkneao*=``$`ena_pknu*J]ia(`ena_pknu%7 y y lner]parke`Bej`?deh`naj$% w bkna]_d$Renpq]hPdaia@ena_pknu`ena_pknuej@ena_pkneao%
363
364
C H APT ER 8 N SITE MA NA G EMENT
w [_deh`naj*=``$`ena_pknu*J]ia(`ena_pknu%7 y bkna]_d$Renpq]hPdaiaBehabehaejBehao% w [_deh`naj*=``$beha*J]ia(beha%7 y y lq^he_>kkha]jCapBehaEoEj_hq`a`$OpnejcbehaJ]ia% w Opnejc_qnnajpOap9PdaiaL]pdLnkre`an*?qnnajp*?qnnajpOap7 eb$BehaEoEj_hq`a`$behaJ]ia(_qnnajpOap(pnqa%% w napqnjpnqa7 y ahoa w napqnjBehaEoEj_hq`a`$behaJ]ia(_qnnajpOap(b]hoa%7 y y lq^he_>kkha]jCap@ena_pknuEoEj_hq`a`$Opnejc`ena_pknuJ]ia% w Opnejc_qnnajpOap9PdaiaL]pdLnkre`an*?qnnajp*?qnnajpOap7 eb$@ena_pknuEoEj_hq`a`$`ena_pknuJ]ia(_qnnajpOap(pnqa%% w napqnjpnqa7 y ahoa w napqnj@ena_pknuEoEj_hq`a`$`ena_pknuJ]ia(_qnnajpOap(b]hoa%7 y y ejpanj]h>kkha]jBehaEoEj_hq`a`$opnejcbehaJ]ia(opnejc_qnnajpOap(± ^kkh_da_g=c]ejopPdaia% w eb$[behao*?kjp]ejoGau$behaJ]ia%% napqnjb]hoa7 Renpq]hPdaiaBehabeha9[behaoWbehaJ]iaY7 eb$$_da_g=c]ejopPdaia% ""$beha*AteopoEjPdaia@ena_pknu% w napqnjb]hoa7 y ahoaeb$$_da_g=c]ejopPdaia% ""$beha*AteopoEjChk^]h@ena_pknu%% w napqnjb]hoa7 y eb$Opnejc*EoJqhhKnAilpu$_qnnajpOap%% napqnjpnqa7 OpnejcbehaAtpajoekj9L]pd*CapAtpajoekj$behaJ]ia%7 eb$behaAtpajoekj*PkQllan$%99*OGEJ xx behaAtpajoekj*PkQllan$%99*?OO
C H A P T E R 8 N S I T E M A N A G E M E N T
xx behaAtpajoekj*PkQllan$%99*FLC% w napqnjpnqa7 y napqnjb]hoa7 y ejpanj]h>kkha]j@ena_pknuEoEj_hq`a`$opnejc`ena_pknuJ]ia(± opnejc_qnnajpOap(± ^kkh_da_g=c]ejopPdaia% w eb$[`ena_pkneao*?kjp]ejoGau$`ena_pknuJ]ia%% napqnjb]hoa7 Renpq]hPdaia@ena_pknu`ena_pknu9[`ena_pkneaoW`ena_pknuJ]iaY7 eb$_da_g=c]ejopPdaia ""`ena_pknu*AteopoEjPdaia@ena_pknu% w napqnjb]hoa7 y ahoaeb$_da_g=c]ejopPdaia ""`ena_pknu*AteopoEjChk^]h@ena_pknu% w napqnjb]hoa7 y napqnjpnqa7 y ejpanj]hRenpq]hPdaiaBehaCapBeha$OpnejcbehaJ]ia% w napqnj[behaoWbehaJ]iaY7 y ejpanj]hRenpq]hPdaia@ena_pknuCap@ena_pknu$Opnejcrenpq]h@en% w eb$[`ena_pkneao*?kqjp99,% napqnjjqhh7 eb$renpq]h@en*Op]npoSepd$Renpq]hL]pd(± Opnejc?kil]neokj*Ejr]ne]jp?qhpqnaEcjkna?]oa%% napqnjjqhh7 Opnejcnah]peraRenpq]hL]pd9renpq]h@en*Oq^opnejc$Renpq]hL]pd*Hajcpd%7 Opnejc`ena_pknuJ]ia9nah]peraRenpq]hL]pd*Oq^opnejc$,(± nah]peraRenpq]hL]pd*Ej`atKb$+%%7 Renpq]hPdaia@ena_pknu_deh`@ena_pknu9[`ena_pkneaoW`ena_pknuJ]iaY7 eb$_deh`@ena_pknu*Renpq]hL]pd99renpq]h@en% napqnj_deh`@ena_pknu7 ahoa napqnj_deh`@ena_pknu*Cap@ena_pknu$renpq]h@en%7 y lq^he_Renpq]hPdaia@ena_pknuL]najp w cap w napqnj[l]najp7 y y
365
366
C H APT ER 8 N SITE MA NA G EMENT
lq^he_>kkha]jAteopo w cap w napqnj$$@ena_pknu*Ateopo$[pdaia=^okhqpaL]pd%% xx$@ena_pknu*Ateopo$[chk^]h=^okhqpaL]pd%%%7 y y lner]pa>kkha]jAteopoEjPdaia@ena_pknu w cap w napqnj$Opnejc*EoJqhhKnAilpu$[pdaia=^okhqpaL]pd%%7 y y lner]pa>kkha]jAteopoEjChk^]h@ena_pknu w cap w napqnj$Opnejc*EoJqhhKnAilpu$[chk^]h=^okhqpaL]pd%%7 y y lq^he_kranne`aEAjqian]^ha@ena_pkneao w cap w napqnj[`ena_pkneao*R]hqao7 y y lq^he_kranne`aEAjqian]^haBehao w cap w napqnj[behao*R]hqao7 y y lq^he_kranne`aEAjqian]^ha?deh`naj w cap w napqnj[_deh`naj*R]hqao7 y y lner]paOpnejcPdaia=^okhqpaL]pd w cap w napqnj[pdaia=^okhqpaL]pd7 y y
C H A P T E R 8 N S I T E M A N A G E M E N T
lner]paOpnejcChk^]h=^okhqpaL]pd w cap w napqnj[chk^]h=^okhqpaL]pd7 y y y The main purpose of this class is to build a copy of the directory and file structure found in the physical theme folder (Path_Themes) and to store references to the files. When the provider retrieves the files, it accesses this virtual structure. Three internal methods are called in the constructor: Bej`Behao, Bej`Oq^@ena_pkneao, and Bej`?deh`naj. The Bej`behao method reads files in the current base folder. Bej`Oq^@ena_pkneao loads directories recursively. In each directory located, the Bej`Behao method is called to load the files for that folder. The Bej`?deh`naj method creates a single combined list of both directories and files. The provider calls the Cap@ena_pknu method to retrieve a specific directory. A simple filter, BehaEoEj_hq`a`, limits the allowed file types. This is exclusive to the handling of themes; other Renpq]hL]pdLnkre`an implementations might require different types. The example ignores all files except for &*ogej, &*_oo, and &*flc, but you can extend the list to support any file type used in your themes. The CapBeha method allows access to a file once it has been located in a directory. The resolving of files and their paths occurs in the compiler module. A simple file list handles all files in all directories. The key contains the full path, so files with the same filename but located in different folders still have a unique full name (see Listing 8-5). Listing 8-5. Implementation of a Custom VirtualPathProvider lq^he_oa]ha`_h]ooPdaiaL]pdLnkre`an6Renpq]hL]pdLnkre`an w lner]paop]pe_PdaiaL]pdLnkre`an[_qnnajpLnkre`an9jqhh7 lner]pa_kjopopnejc=OLJapPdaia>]oaL]pd9+=ll[Pdaiao+7 lner]paopnejc[pdaiaNah]peraL]pd9Opnejc*Ailpu7 lner]paopnejc[_qnnajpPdaiaOap9Opnejc*Ailpu7 lner]paopnejc[chk^]hPdaiaJ]ia9Opnejc*Ailpu7 lner]paPdaiaL]pdLnkre`an$% w [pdaiaNah]peraL]pd9± Sa^?kjbecqn]pekjI]j]can*=llOappejcoW?qopkiPdaia>]oaL]pdY7 y lq^he_kranne`aOuopai*Sa^*?]_dejc*?]_da@alaj`aj_uCap?]_da@alaj`aj_u$± opnejcrenpq]hL]pd(± Ouopai*?khha_pekjo*EAjqian]^harenpq]hL]pd@alaj`aj_eao(± @]paPeiaqp_Op]np% w napqnjjqhh7 y lq^he_kranne`a^kkh@ena_pknuAteopo$opnejcrenpq]h@en% w eb$renpq]h@en*Ej`atKb$=OLJapPdaia>]oaL]pd%99)-% napqnj^]oa*@ena_pknuAteopo$renpq]h@en%7
367
368
C H APT ER 8 N SITE MA NA G EMENT
Renpq]hPdaia@ena_pknu`ena_pknu9Cap@ena_pknu$renpq]h@en%]o± Renpq]hPdaia@ena_pknu7 napqnj`ena_pknu*Ateopo7 y lq^he_kranne`a^kkhBehaAteopo$opnejcrenpq]hL]pd% w eb$renpq]hL]pd*Ej`atKb$=OLJapPdaia>]oaL]pd%99)-% napqnj^]oa*BehaAteopo$renpq]hL]pd%7 opnejcbehaJ]ia9Ouopai*Sa^*Renpq]hL]pdQpehepu*CapBehaJ]ia$renpq]hL]pd%7 opnejcrenpq]h@ena_pknuL]pd9± Ouopai*Sa^*Renpq]hL]pdQpehepu*Cap@ena_pknu$renpq]hL]pd%7 Renpq]hPdaia@ena_pknu`ena_pknu9± Cap@ena_pknu$renpq]h@ena_pknuL]pd%]oRenpq]hPdaia@ena_pknu7 napqnj`ena_pknu*CapBehaEoEj_hq`a`$behaJ]ia%7 y lq^he_kranne`aRenpq]h@ena_pknuCap@ena_pknu$opnejcrenpq]h@en% w eb$renpq]h@en*Ej`atKb$=OLJapPdaia>]oaL]pd%99)-% napqnj^]oa*Cap@ena_pknu$renpq]h@en%7 eb$EoPdaia@ena_pknuRenpq]hL]pd$renpq]h@en%% w napqnjjasRenpq]hPdaia@ena_pknu$renpq]h@en%7 y ahoa w OpnejcpdaiaRenpq]hL]pd9CapPdaia@ena_pknuRenpq]hL]pd$renpq]h@en%7 Renpq]hPdaia@ena_pknu`ena_pknu9jasRenpq]hPdaia@ena_pknu$renpq]h@en%7 napqnj`ena_pknu*Cap@ena_pknu$renpq]h@en%7 y y lq^he_kranne`aRenpq]hBehaCapBeha$opnejcrenpq]hL]pd% w eb$renpq]hL]pd*Ej`atKb$=OLJapPdaia>]oaL]pd%99)-% napqnj^]oa*CapBeha$renpq]hL]pd%7 Opnejcrenpq]h@ena_pknuL]pd9± Ouopai*Sa^*Renpq]hL]pdQpehepu*Cap@ena_pknu$renpq]hL]pd%7 Renpq]hPdaia@ena_pknu`ena_pknu9± Cap@ena_pknu$renpq]h@ena_pknuL]pd%]oRenpq]hPdaia@ena_pknu7 OpnejcbehaJ]ia9Ouopai*Sa^*Renpq]hL]pdQpehepu*CapBehaJ]ia$renpq]hL]pd%7 napqnj`ena_pknu*CapBeha$behaJ]ia%7 y lner]paOpnejc?khha_pekjCap@alaj`ajp@ena_pkneao$ Opnejcl]najp@ena_pknuL]pd( Opnejc?khha_pekj`alaj`ajpL]pdo% w OpnejcWY`ena_pkneao9@ena_pknu*Cap@ena_pkneao$l]najp@ena_pknuL]pd%7 bkn$ejphkklEj`at9,7hkklEj`at8`ena_pkneao*Hajcpd7hkklEj`at''% w `alaj`ajpL]pdo*=``$`ena_pkneaoWhkklEj`atY%7 Cap@alaj`ajp@ena_pkneao$`ena_pkneaoWhkklEj`atY(`alaj`ajpL]pdo%7 y napqnj`alaj`ajpL]pdo7 y
C H A P T E R 8 N S I T E M A N A G E M E N T
lner]pa>kkha]jEoPdaia@ena_pknuRenpq]hL]pd$Opnejcrenpq]hL]pd% w Opnejcl]najpRenpq]hL]pd9± Ouopai*Sa^*Renpq]hL]pdQpehepu*Cap@ena_pknu$renpq]hL]pd%7 napqnjl]najpRenpq]hL]pd*Aj`oSepd$=OLJapPdaia>]oaL]pd(± Opnejc?kil]neokj*Ejr]ne]jp?qhpqnaEcjkna?]oa%7 y lner]paOpnejcCapPdaia@ena_pknuRenpq]hL]pd$Opnejcrenpq]hL]pd% w Opnejcl]najpRenpq]hL]pd9± Renpq]hL]pdQpehepu*Cap@ena_pknu$renpq]hL]pd%7 sdeha$EoPdaia@ena_pknuRenpq]hL]pd$l]najpRenpq]hL]pd%% w l]najpRenpq]hL]pd9± Ouopai*Sa^*Renpq]hL]pdQpehepu*Cap@ena_pknu$l]najpRenpq]hL]pd%7 y napqnjl]najpRenpq]hL]pd7 y ejpanj]hOpnejc?kjranpPkPdaiaNah]peraL]pd$Opnejcnah]peraL]pd% w napqnj?kjranpPkPdaiaJ]iaNah]peraL]pd$nah]peraL]pd(b]hoa%7 y ejpanj]hOpnejc?kjranpPkChk^]hNah]peraL]pd$Opnejcnah]peraL]pd% w napqnj?kjranpPkPdaiaJ]iaNah]peraL]pd$nah]peraL]pd(pnqa%7 y lner]paOpnejc?kjranpPkPdaiaJ]iaNah]peraL]pd$Opnejcnah]peraL]pd(± >kkha]jnalh]_aPdaiaJ]iaSepdChk^]h% w OpnejcpdaiaJ]iaNah]peraL]pd9Opnejc*Ailpu7 eb$$nah]peraL]pd*Op]npoSepd$=OLJapPdaia>]oaL]pd%% ""$[pdaiaNah]peraL]pd*Op]npoSepd$+%%% w pdaiaJ]iaNah]peraL]pd9nah]peraL]pd*Oq^opnejc$,(± nah]peraL]pd*Ej`atKb$=OLJapPdaia>]oaL]pd%%7 y eb$$pdaiaJ]iaNah]peraL]pd*Aj`oSepd$+%% ""$[pdaiaNah]peraL]pd*Op]npoSepd$+%%% w pdaiaJ]iaNah]peraL]pd9± Ouopai*Sa^*Renpq]hL]pdQpehepu*± =llaj`Pn]ehejcOh]od$pdaiaJ]iaNah]peraL]pd%7 y pdaiaJ]iaNah]peraL]pd'9[pdaiaNah]peraL]pd7 Opnejcnai]ej`anL]pd9nah]peraL]pd*Oq^opnejc$± nah]peraL]pd*Ej`atKb$=OLJapPdaia>]oaL]pd%'± =OLJapPdaia>]oaL]pd*Hajcpd%7 eb$nalh]_aPdaiaJ]iaSepdChk^]h%
369
370
C H APT ER 8 N SITE MA NA G EMENT
w nai]ej`anL]pd9nai]ej`anL]pd*Oq^opnejc$nai]ej`anL]pd*Ej`atKb$+%%7 y pdaiaJ]iaNah]peraL]pd'9nai]ej`anL]pd7 napqnjpdaiaJ]iaNah]peraL]pd7 y lq^he_op]pe_PdaiaL]pdLnkre`an?qnnajp w cap w eb$[_qnnajpLnkre`an9jqhh% napqnj[_qnnajpLnkre`an7 [_qnnajpLnkre`an9jasPdaiaL]pdLnkre`an$%7 napqnj[_qnnajpLnkre`an7 y y lq^he_Opnejc?qnnajpOap w cap w napqnj[_qnnajpPdaiaOap7 y oap w [_qnnajpPdaiaOap9r]hqa7 y y y The Cap?]_da@alaj`aj_u returns jqhh in order to suppress any internal caching. You must either return jqhh or fully implement the caching. Otherwise, the compiler will try to resolve the default path (App_Themes) from the cache and this will fail. Two methods are called when the compiler tries to resolve an internal path: Cap@ena_pknu and CapBeha. If a file is referenced in a page that points to a theme, the compiler asks the provider to retrieve the same. Using the CapBeha method, we first check whether the theme’s folder is being used. This is the folder for which the provider in the example is responsible. From the file’s name, the containing directory is built. The directory is used to obtain the Renpq]hPdaia@ena_pknu implementation, which in turn returns the file held there. At the core of this implementation is a path manipulation algorithm. It could be far more sophisticated than the simple code in the example. Before a folder or file is retrieved, the @ena_pknuAteopo and BehaAteopo methods are called. This allows the provider to programmatically “hide” parts of the structure or to check for the physical presence of the requested resource. The basic approach is always the same. The calling instance “asks” the provider for a physical file. The provider returns a Renpq]hBeha instance, which allows direct access to the file’s contents via a Opna]i object. The remarkable thing is that the file might not necessarily exist anywhere on the file system. If, for example, the stream is obtained from a database call, the Renpq]hL]pdLnkre`an is simulating a physical file system. The “Exists” methods could always return pnqa in order to simulate a system where the page developers can use any value, and the system returns a set of globally predefined resources.
C H A P T E R 8 N S I T E M A N A G E M E N T
Configuring the Path Provider The provider requires two final configuration steps. First, it must have a setting in web.config to show that it’s configurable. Second, the provider must be registered, which follows the pattern previously explained. To demonstrate, I’ve used the =llhe_]pekj[Op]np event handler defined in the global.asax file, as shown in Listing 8-6. Listing 8-6. Configuration Settings in web.config 8]llOappejco: 8]``gau9?qopkiPdaia>]oaL]pdr]hqa9+L]pd[Pdaiao++: 8+]llOappejco: In Listing 8-7, the key is used to configure the physical path. Listing 8-7. Registering the Path Provider Using the Application_Start event lnkpa_pa`rke`=llhe_]pekj[Op]np$k^fa_poaj`an(Arajp=ncoa% w DkopejcAjrenkjiajp*NaceopanRenpq]hL]pdLnkre`an$PdaiaL]pdLnkre`an*?qnnajp%7 y The registration uses a singleton instance of the provider instead of the constructor. This results in slightly shorter code. A singleton instance works well because the provider works globally and exists only once in the application.
Limitations of the VirtualPathProvider Approach The Renpq]hL]pdLnkre`an is deeply integrated in the ASP.NET engine. Although it’s very powerful, certain tasks can be problematic. In this section, I explain two typical issues found when working with custom path providers.
Working with LoadControl The Renpq]hL]pdLnkre`an allows you to override the way in which tilde-based paths are resolved in page directives: 8!nksoan. If a browser supports HTML 4.0, the page framework should compose 8(4-,5SINGweb.config, you can configure this behavior and replace the Dpih/.PatpSnepan with an TDpihPatpSnepan. The >nksoan property of the DpplNamqaop class returns an Dppl>nksoan?]l]^ehepeao object. The underlying code is relatively straightforward. If a custom PatpSnepan is defined, the ?na]paDpihPatpSnepanBnkiPula is used. If not, the default Dpih/.PatpSnepan is used. The following code snippets are decompiled from the Ouopai*Sa^*DpplNamqaop class and the Ouopai*Sa^*QE*L]ca class, respectively. ejpanj]hDpihPatpSnepan?na]paDpihPatpSnepanEjpanj]h$PatpSnepanps% w Pulap]cSnepan9pdeo*P]cSnepan7 eb$p]cSnepan9jqhh% w napqnjL]ca*?na]paDpihPatpSnepanBnkiPula$ps(p]cSnepan%7 y napqnjjasDpih/.PatpSnepan$ps%7 y lq^he_PulaP]cSnepan w cap w pnu w eb$pdeo*[d]rap]csnepan% w opnejcopn9pdeoWp]csnepanY7 eb$opnejc*EoJqhhKnAilpu$opn%% w pdeo*[p]csnepan9jqhh7 y ahoaeb$opnejc*?kil]na$opn(pulakb$DpihPatpSnepan%*BqhhJ]ia(± Opnejc?kil]neokj*Kn`ej]h%99,% w
C H A P T E R 9 N C O N T R O L E X T E N S I B I LI T Y
pdeo*[p]csnepan9pulakb$DpihPatpSnepan%7 y ahoa w pdeo*[p]csnepan9>qeh`I]j]can*CapPula$opn(pnqa%7 y pdeo*[d]rap]csnepan9pnqa7 y y _]p_d$At_alpekjat_alpekj% w pdnkspdeo*>qeh`L]noaAnnkn$at_alpekj(p]csnepan%7 y napqnjpdeo*[p]csnepan7 y y lq^he_op]pe_DpihPatpSnepan?na]paDpihPatpSnepanBnkiPula$PatpSnepanps(± PulasnepanPula% w DpihPatpSnepansnepan7 eb$snepanPula99pulakb$DpihPatpSnepan%% w napqnjjasDpihPatpSnepan$ps%7 y eb$snepanPula99pulakb$Dpih/.PatpSnepan%% w napqnjjasDpih/.PatpSnepan$ps%7 y pnu w Qpeh*?da_g=ooecj]^haPula$pulakb$DpihPatpSnepan%(snepanPula%7 snepan9$DpihPatpSnepan%DpplNqjpeia*?na]paJkjLq^he_Ejop]j_a$± snepanPula(jask^fa_pWYwpsy%7 y _]p_d w pdnksjasDpplAt_alpekj$ON*CapOpnejc$Ejr]he`[DpihPatpSnepan(± jask^fa_pWYwsnepanPula*BqhhJ]iay%%7 y napqnjsnepan7 y As you can see, the instance of the abstract PatpSnepan class is responsible for the rendering process. Because ASP.NET produces HTML, the DpihPatpSnepan class is the optimum point for beginning to implement a custom writer class. The TdpihPatpSnepan and ?dpihPatpSnepan types mentioned earlier derive from the DpihPatpSnepan and Dpih/.PatpSnepan respectively. They are concrete implementations for specific rendering behavior. See Figure 9-1.
375
376
C H APT ER 9 N CONTR OL EX TENS IB IL ITY
Figure 9-1. The control adapter architecture
Using Control Adapters To modify the behavior described earlier, you’ll need an implementation of the Ouopai*Sa^* QE*=`]lpano*?kjpnkh=`]lpan base class. In each phase of the life cycle, the control checks whether or not an adapter is available. If there is one present, the adapter provides an alternative method that replaces the default method that would otherwise be called in that step in the life cycle. If the adapter only modifies portions of the behavior, it can call the control’s default method instead. Adapters that modify the state persistence behavior differ in that they don’t modify, but completely replace, the default behavior. When the adapter intercepts the life cycle, the following actions (at a minimum) must occur:
s /VERRIDETHEKjEjep method of the control adapter to modify the initializing phase.
s /VERRIDETHENaj`an or Naj`an?deh`naj method to add custom markup. The abstract base class has the following structure:
lq^he_]^opn]_p_h]oo?kjpnkh=`]lpan w lnkpa_pa`?kjpnkh=`]lpan$%7 lnkpa_pa`Dppl>nksoan?]l]^ehepeao>nksoanwcap7y lnkpa_pa`?kjpnkh?kjpnkhwcap7y lnkpa_pa`L]caL]cawcap7y lnkpa_pa`L]ca=`]lpanL]ca=`]lpanwcap7y lnkpa_pa`ejpanj]hrenpq]hrke`>acejNaj`an$DpihPatpSnepansnepan%7 lnkpa_pa`ejpanj]hrenpq]hrke`Aj`Naj`an$DpihPatpSnepansnepan%7 lnkpa_pa`ejpanj]hrenpq]hrke`Hk]`=`]lpan?kjpnkhOp]pa$k^fa_pop]pa%7 lnkpa_pa`ejpanj]hrenpq]hrke`Hk]`=`]lpanReasOp]pa$k^fa_pop]pa%7 lnkpa_pa`ejpanj]hrenpq]hrke`KjEjep$Arajp=ncoa%7 lnkpa_pa`ejpanj]hrenpq]hrke`KjHk]`$Arajp=ncoa%7 lnkpa_pa`ejpanj]hrenpq]hrke`KjLnaNaj`an$Arajp=ncoa%7 lnkpa_pa`ejpanj]hrenpq]hrke`KjQjhk]`$Arajp=ncoa%7 lnkpa_pa`ejpanj]hrenpq]hrke`Naj`an$DpihPatpSnepansnepan%7 lnkpa_pa`renpq]hrke`Naj`an?deh`naj$DpihPatpSnepansnepan%7 lnkpa_pa`ejpanj]hrenpq]hk^fa_pO]ra=`]lpan?kjpnkhOp]pa$%7
C H A P T E R 9 N C O N T R O L E X T E N S I B I LI T Y
lnkpa_pa`ejpanj]hrenpq]hk^fa_pO]ra=`]lpanReasOp]pa$%7 y Before you start implementing adapters to change a control’s behavior, you’ll need to learn the purpose for each property and method (see Table 9-1). Table 9-1. The ControlAdapter Base Class
Class Member
Description
>nksoan
The browser capabilities (Dppl>nksoan?]l]^ehepeao) of the client making the current HTTP request.
?kjpnkh
The control to which this control adapter is attached.
L]ca
The page containing the control associated with this adapter.
L]ca=`]lpan
The page adapter (Ouopai*Sa^*QE*=`]lpano*L]ca=`]lpan) for the page defined by L]ca.
>acejNaj`an
#ALLEDPRIORTOTHERENDERINGOFACONTROL4HEMETHODGENERATES opening tags required by a specific target. It takes a Ouopai*Sa^* QE*DpihPatpSnepan object to render the target-specific output.
Aj`Naj`an
#ALLEDAFTERTHERENDERINGOFACONTROL4HEMETHODGENERATES closing tags that are required by a specific target. It takes a Ouopai* Sa^*QE*DpihPatpSnepan object to render the target-specific output.
?na]pa?deh`?kjpnkho
#REATESTHETARGET SPECIFICCHILDCONTROLSFORACOMPOSITECONTROL
Hk]`=`]lpan?kjpnkhOp]pa
Loads adapter control state information saved by the Ouopai*Sa^* QE*=`]lpano*?kjpnkh=`]lpan*O]ra=`]lpan?kjpnkhOp]pa method during a previous request to the L]ca. Receives the state as a Op]pa>]c object.
Hk]`=`]lpanReasOp]pa
Loads adapter view state information saved by the Ouopai*Sa^*QE* =`]lpano*?kjpnkh=`]lpan*O]ra=`]lpanReasOp]pa method during a previous request to the L]ca. Receives the state as a Op]pa>]c object.
KjEjep
/VERRIDESTHEOuopai*Sa^*QE*?kjpnkh*KjEjep method for the associated control in order to hook into the initializing phase.
KjHk]`
/VERRIDESTHEOuopai*Sa^*QE*?kjpnkh*KjHk]` method for the associated control in order to hook into the load phase.
KjLnaNaj`an
/VERRIDESTHEOuopai*Sa^*QE*?kjpnkh*KjLnaNaj`an method for the associated control.
KjQjhk]`
/VERRIDESTHEOuopai*Sa^*QE*?kjpnkh*KjQjhk]` method for the associated control.
Naj`an
Generates target-specific markup for the control to which the adapter is attached. Takes a Ouopai*Sa^*QE*DpihPatpSnepan object to render the target-specific output.
Naj`an?deh`naj
Generates the target-specific markup for the child controls in a composite control to which the adapter is attached. Takes a Ouopai*Sa^* QE*DpihPatpSnepan object to render the target-specific output.
O]ra=`]lpan?kjpnkhOp]pa
Saves control state information for the control adapter into a Op]pa>]c object.
O]ra=`]lpanReasOp]pa
Saves view state information for the control adapter into a Op]pa>]c object.
You now have all the information you need in order to implement a custom control adapter. (OWEVER BEFORETHEADAPTERCANBEUSED ITMUSTBECONFIGURED#ONFIGURATIONDEPENDSONADEVICE filter.
377
378
C H APT ER 9 N CONTR OL EX TENS IB IL ITY
The Ouopai*Sa^*QE*Sa^?kjpnkho*=`]lpano*Sa^?kjpnkh=`]lpan base class is the preferred way to change the behavior of built-in web controls. This class adds virtual methods Naj`an>acejP]c, Naj`anAj`P]c, and Naj`an?kjpajpo, to more closely mirror the render behavior of web controls. lq^he__h]ooSa^?kjpnkh=`]lpan6?kjpnkh=`]lpan w lnkpa_pa`Sa^?kjpnkh?kjpnkhwcap7y lnkpa_pa`^kkhEoAj]^ha`wcap7y lnkpa_pa`renpq]hrke`Naj`an>acejP]c$DpihPatpSnepansnepan%7 lnkpa_pa`renpq]hrke`Naj`an?kjpajpo$DpihPatpSnepansnepan%7 lnkpa_pa`renpq]hrke`Naj`anAj`P]c$DpihPatpSnepansnepan%7 y Table 9-2 explains its properties and methods. Table 9-2. WebControlAdapter
Member
Description
?kjpnkh
The web control to which this adapter is attached
EoAj]^ha`
Indicates whether the web control and all its parent controls are enabled
Naj`an
Generates the target-specific markup for the control to which the adapter is attached
Naj`an>acejP]c
#REATESTHEBEGINNINGTAGINTHEMARKUPFORTHEWEBCONTROL
Naj`an?kjpajpo
Generates the target-specific inner markup for the web control to which the adapter is attached
Naj`anAj`P]c
#REATESTHECLOSINGTAGINTHEMARKUPFORTHEWEBCONTROL
Note that the Sa^?kjpnkh=`]lpan is not an abstract class, but the basic class in the hierarchy of adapters responsible for regular web controls. It’s in the Ouopai*Sa^*QE*Sa^?kjpnkho*=`]lpano namespace. The Sa^?kjpnkh=`]lpan class is the base class for the following implementations:
s Dean]n_de_]h@]p]>kqj`?kjpnkh=`]lpan
s @]p]>kqj`?kjpnkh=`]lpan
s De`a@eo]^ha`?kjpnkh=`]lpan
s Iajq=`]lpan
The Iajq=`]lpan is a concrete implementation used to render Iajq controls. The Dean]n_de_]h@]p]>kqj`?kjpnkh=`]lpan provides a virtual method, Lanbkni@]p]>ej`ejc, which calls ?kjpnkh*Lanbkni@]p]>ej`ejc/VERRIDINGTHISMETHODINADERIVEDCLASSALLOWSYOU to change the binding behavior. (This is also the case for the @]p]>kqj`?kjpnkh=`]lpan class.) The De`a@eo]^ha`?kjpnkh=`]lpanCANBEAPPLIEDTOANYCONTROL5SINGTHISADAPTERREMOVESTHECONTROL from pages in which the control is disabled. It overrides the adapter’s Naj`an method and, if its Aj]^ha` property is false, the control’s Naj`an method is not called, as shown in Figure 9-2.
C H A P T E R 9 N C O N T R O L E X T E N S I B I LI T Y
Figure 9-2. The class diagram for control adapter classes The base classes can be used to perform basic tasks without you having to implement your own adapter. With them you can also vary a single behavior, such as data binding, while benefiting from all the other default adapter functionality. In the class diagram, you’ll also find a L]ca=`]lpan class that derives from ?kjpnkh=`]lpan. Thus, pages can have adapters, too.
Using Page Adapters As a L]ca is essentially a specialized ?kjpnkh, the control adapter also supports pages. The L]ca=`]lpan base class exists to make writing a custom page adapter easy. This base class inherits from the ?kjpnkh=`]lpan described earlier and extends the adapter with page-specific features. lq^he_]^opn]_p_h]ooL]ca=`]lpan6?kjpnkh=`]lpan w lq^he_renpq]hOpnejc?khha_pekj?]_daR]nu>uDa]`anowcap7y lq^he_renpq]hOpnejc?khha_pekj?]_daR]nu>uL]n]iowcap7y lnkpa_pa`opnejc?heajpOp]pawcap7y lq^he_renpq]hJ]iaR]hqa?khha_pekj@apaniejaLkop>]_gIk`a$%7 lnkpa_pa`ejpanj]hrenpq]hopnejcCapLkop>]_gBkniNabanaj_a$opnejcbkniE`%7 lq^he_renpq]hE?khha_pekjCapN]`ek>qppkjo>uCnkql$opnejccnkqlJ]ia%7 lq^he_renpq]hL]caOp]paLanoeopanCapOp]paLanoeopan$%7
379
380
C H APT ER 9 N CONTR OL EX TENS IB IL ITY
lq^he_renpq]hrke`NaceopanN]`ek>qppkj$N]`ek>qppkjn]`ek>qppkj%7 lq^he_renpq]hrke`Naj`an>acejDulanhejg$DpihPatpSnepansnepan(± opnejcp]ncapQnh(± ^kkhaj_k`aQnh(± opnejcokbpgauH]^ah%7 lq^he_renpq]hrke`Naj`an>acejDulanhejg$DpihPatpSnepansnepan(± opnejcp]ncapQnh(± ^kkhaj_k`aQnh(± opnejcokbpgauH]^ah(± opnejc]__aooGau%7 lq^he_renpq]hrke`Naj`anAj`Dulanhejg$DpihPatpSnepansnepan%7 lq^he_renpq]hrke`Naj`anLkop>]_gArajp$DpihPatpSnepansnepan(± opnejcp]ncap(± opnejc]ncqiajp(± opnejcokbpgauH]^ah(± opnejcpatp%7 lq^he_renpq]hrke`Naj`anLkop>]_gArajp$DpihPatpSnepansnepan(± opnejcp]ncap(± opnejc]ncqiajp(± opnejcokbpgauH]^ah(± opnejcpatp(± opnejclkopQnh(± opnejc]__aooGau%7 lnkpa_pa`rke`Naj`anLkop>]_gArajp$DpihPatpSnepansnepan(± opnejcp]ncap(± opnejc]ncqiajp(± opnejcokbpgauH]^ah(± opnejcpatp(± opnejclkopQnh(± opnejc]__aooGau(± ^kkhaj_k`a%7 lq^he_renpq]hopnejcPn]jobkniPatp$opnejcpatp%7 y Table 9-3 explains the properties and methods provided by the L]ca=`]lpan. (The L]ca=`]lpan attaches to a web page. This is the “page” referred to in the table descriptions.) The L]ca=`]lpan supports the persistence layer and the render behavior of controls that group multiple tags. N]`ek>qppkjo need special treatment when handling groups. You can define a group of radio buttons in HTML by giving them the same name. However, ASP.NET requires different IDs for each radio button, and by default the name and ID are the same. The L]ca=`]lpan will thus render each radio button group appropriately to keep the groups separate. The CapOp]paLanoeopan property can be overloaded in order to change the view state persister GLOBALLY5SINGTHEABSTRACTBASECLASS YOUCANWRITEACUSTOMPAGEADAPTERTOCHANGETHEBEHAVIOR of all pages. Why does the rendering of a page depend on the client device? Remember that the view state has a strong influence on the client. The view state could grow and consume a significant portion OFTHEPAGESRENDEREDCONTENT2EFERTO#HAPTERFORAREFRESHERABOUTVIEWSTATE 4HEUSEREXPErience on mobile devices and smart phones generally deteriorates with large pages. To mitigate THIS YOUCOULDWRITEAPAGEADAPTERTHATCREATESAREGULARVIEWSTATEFOR0# BASEDBROWSERSAND server-side view state for mobile devices. The implication is that, as for ?kjpnkh=`]lpan implementations, the L]ca=`]lpan must be configured and associated with a set of clients.
C H A P T E R 9 N C O N T R O L E X T E N S I B I LI T Y
Table 9-3. Properties and Methods in the PageAdapter, Additional to Those Provided by the ControlAdapter
Member
Description
?]_daR]nu>uDa]`ano
A list (of type EHeop) of additional HTTP headers by which caching is varied for the web page.
?]_daR]nu>uL]n]io
A list (of type EHeop) of additional parameters from HTTP GET and 0/34REQUESTSBYWHICHCACHINGISVARIEDFORTHEWEBPAGE
?heajpOp]pa
An encoded string containing the view and control state data of the web page.
@apaniejaLkop>]_gIk`a
Evaluates whether the web page is in postback mode and returns a name/value collection of type Ouopai*?khha_pekjo*Ola_e]heva`* J]iaR]hqa?khha_pekj of the postback variables.
CapLkop>]_gBkniNabanaj_a
Returns a DHTML code fragment that the client browser can use to reference the form on the page that was posted.
CapN]`ek>qppkjo>uCnkql
Retrieves a collection of radio button controls specified by cnkqlJ]ia. Takes the name of a Ouopai*Sa^*QE*Sa^?kjpnkho* N]`ek>qppkj group and returns an E?khha_pekj object with N]`ek>qppkj instances.
CapOp]paLanoeopan
Returns a L]caOp]paLanoeopan object used by the web page to maintain the control and view states.
NaceopanN]`ek>qppkj
Adds a radio button control to the collection for a specified radio button group.
Naj`an>acejDulanhejg
2ENDERSANOPENINGHYPERLINKTAG INCLUDINGTHETARGET52, TOTHE response stream. It takes the current PatpSnepan instance to write the data. This method is overloaded in order to support different sets of parameters.
Naj`anAj`Dulanhejg
Renders a closing hyperlink tag to the response stream.
Naj`anLkop>]_gArajp
Renders a postback event to the response stream as a hyperlink, including the encoded and possibly encrypted view state, and the event target and argument. This method is overloaded to support different sets of parameters.
Pn]jobkniPatp
Transforms text for the target browser.
Device-Specific Filter for Adaptive Behavior A device filter recognizes a client device and assigns a device-specific adapter to its controls. The filter can also identify control properties, user defined attributes and templates, and can be controlled with