Todotnet Blog

Met de blik op Visual Studio 2005 en verder...

  Home :: Contact :: Syndication  :: Login
  69 Posts :: 3 Stories :: 27 Comments :: 32 Trackbacks

News

Het Provider Model in .NET 2.0

Versie 2.0 van het .NET Framework bevat nieuwe functionaliteit in de vorm van providers. Deze providers stellen ons in staat om in applicaties generieke functionaliteit te implementeren en deze implementatie dynamisch te wisselen. Dat wil zeggen, een functie aanroepen, zonder te weten hoe deze functie geïmplementeerd is. En als de implementatie niet geschikt is voor ons toepassing kunnen we deze wisselen voor een andere implementatie zonder aanpassingen in de aanroepende code.

In dit artikel laten we zien wat de fundamenten zijn van het Provider model en hoe je zelf een provider-gebaseerde feature kunt toevoegen aan je applicatie.

Features

In het Provider model van .NET 2.0 wordt ook gerefereerd aan ‘features’. Een feature van het nieuwe framework is bijvoorbeeld Membership, of Health Monitoring. In essentie bestaat een feature uit een logische set van functies, meestal samengepakt in één klasse. Wanneer we het in de rest van dit artikel over features hebben, dan bedoelen we dus een groep functies die logisch gegroepeerd en direct vanuit programmacode in een client gebruikt kunnen worden.

Pattterns

Het Provider model maakt gebruik van een viertal ontwerptechnieken, ook wel patterns. De belangrijkste is de Strategy pattern. In het kort zorgt het Strategy pattern voor de afscherming van de functionaliteit van een feature zodat de implementatie van zo’n feature kan veranderen zonder aanpassingen in de code die gebruik maakt van deze feature.

Strategy Pattern

Wanneer je dit Strategy pattern wil gebruiken in je applicatie is het ontwerp van de feature erg belangrijk. Het moet duidelijk zijn welke functionaliteit zichtbaar is, zodat verschillende implementaties van deze feature in staat zijn deze functionaliteit te leveren. Daarnaast is het essentieel dat de ene implementatie niet additionele afhankelijkheden oproept die vanuit de programmacode van de client ingevuld moeten worden. Denk hierbij bijvoorbeeld aan een connectiestring, een gebruikersnaam, wachtwoorden e.d.

Factory Method

Het Strategy pattern zou niet zo praktisch zijn als we niet in staat waren eenvoudig te wisselen tussen de ene en de andere implementatie. De Factory method is daarom een logische toevoeging om het principe van Providers te laten werken. De gedachte achter de Factory method is dat er een scheiding moet zijn tussen de instantiatie van klassen en de feature die er gebruik van maakt. Andersgezegd, er moeten geen harde verwijzingen zijn naar de concrete types in de client.

Deze losse koppeling kan geïmplementeerd worden door het gebruik van interfaces of abstracte klassen. De feature-klasse kan zodoende de methoden uit de interface of de abstracte klassen aanspreken zonder te weten hoe deze geïmplementeerd is. Maar we moeten wel ergens vastleggen welke feitelijke klasse de gewenste functie zal uitvoeren.

Gelukkig beschikt het .NET Framework over allerlei mogelijkheden om vanuit een string-waarde een bepaalde klasse in een assembly op te zoeken. Dit heet ook wel ‘reflection’. In combinatie met de configuratiemogelijkheden in XML bestanden die bij een applicatie horen kunnen we de definitie van de feature ‘Provider’ vastleggen en eenvoudig veranderen. Hoe dat precies in zijn werk gaat zien we later in het verhaal.

Singleton Pattern

Een derde ontwerptechniek die van belang is voor het Provider model is het Singleton pattern. Deze gebruiken we wanneer het belangrijk is om slechts één instantie van een bepaalde klasse in een applicatie actief te hebben.

Dat verschillende redenen hebben. Zo kan de instantiatie van zo’n klasse kostbaar zijn in termen van tijd en processorkracht. Daarnaast is het soms van belang om toegang tot een bepaalde bron, bijvoorbeeld een database, te laten verlopen via één specifiek object, bijvoorbeeld om concurrency problemen te voorkomen. De meeste ASP.NET 2.0 providers implementeren wel het Singleton pattern maar beschikken niet over specifieke lock-mechanismen om gelijktijdige toegang tot bronnen te voorkomen. Dat komt omdat bij de aanroep van methoden er maar weinig gedeelde gegevens zijn. En als er al gegevens gedeeld worden, is dat alleen om te lezen.

Façade

Het laatste pattern dat in het Provider model wordt gebruikt is de Façade. Deze ontwerp-techniek is er voor bedoeld om ontwikkelaars gemak te bieden bij het aanroepen van functionaliteit van een bepaalde feature. Je zou het de ‘grootste gemene deler’ API kunnen noemen die enkel die methoden aanbiedt die het meest voor de hand liggen en de uitgebreidere, complexe methoden afschermt. Zo is de statische klasse Membership een typisch voorbeeld van zo’n Façade. Er zitten vier overloaded methoden in voor ‘CreateUser’ terwijl de MembershipProvider er maar één kent. Wanneer de ontwikkelaar er één kiest uit Membership, zorgt deze klasse voor het invullen van de eventueel ontbrekende waarden.

Provider klassen

De belangrijkste provider klassen bevinden zich in de System.Configuration.Provider namespace. Hieruit zou je kunnen afleiden dat het Provider model niet enkel voor ASP.NET 2.0 toepasbaar is, en dat is ook zo.

Alle providers erven van ProviderBase, een klasse met een tamelijke summiere definitie: een Name en Description eigenschap en één methode, Initialize. ProviderBase is een abstracte klasse omdat, gezien de beperkte functionaliteit, het direct gebruiken van deze klasse weinig zinvol is. Het belangrijkste element van ProviderBase is de Initialize-methode. Deze methode wordt door de statische feature klassen (zoals Membership) aangeroepen via de ProvidersHelper klasse en zorgt er voor dat de provider klasse geheel up en running is, en tevens maar één keer kan worden geïnstantieerd.

De Initalize methode heeft twee parameters. Name verwijst naar het name attribuut in de configuratie en config is een NameValueCollection die gevuld is met waarden uit het <add /> element behorend bij de specifieke provider definitie.

Een beetje historie

Het ASP.NET team is verantwoordelijk voor het Provider model. Er is met dit team een discussie geweest over de relatief beperkte set aan parameter-mogelijkheden voor een provider. De parameters die een provider nodig heeft kunnen immers enkel bestaan uit naam/waarde paren. Waarom is er geen gebruik gemaakt van een XmlNode of een XPathNavigator? En waarom is een geen gebruik gemaakt van de nieuwe Generics mogelijkheden van het 2.0 Framework.

De reden om geen xml boomstructuur te gebruiken is niet verklaard. Dat neemt niet weg dat het referen naar een dergelijke structuur in een naam/waarde collectie prima mogelijk is.

Het niet gebruiken van Generics heeft vooral te maken met het moment waarop het Provider model gestalte kreeg in .NET 2.0. Dat was vrij vroeg en gebaseerd op de mogelijkheden van .NET 1.1. De opzet van geconfigureerde Providers komt ook al terug in de Enterprise Library en ook de open source portal DotNetNuke. Het idee van Providers is dus allerminst nieuw of revolutionair te noemen. Daar komt bij dat Generics in eerste instantie geen onderdeel waren van de Common Language Specification (CLS) en de Microsoft regel is dat alle features van het .NET Framework zelf CLS compliant moeten zijn. Nadat het Provider model al bijna helemaal klaar was is Generics alsnog toegetreden tot de CLS.

Het is dus goed mogelijk dat in toekomstige versies nieuwe mogelijkheden van het Provider model worden toegevoegd aan de hand van Generics en uitgebreide configuratie-opties.

ProvidersHelper

De Providers klassen zitten, zoals eerder gezegd, in de System.Configuration.Providers namespace. Wie deze namespace bekijkt, ziet eigenlijk maar drie klassen:

  • ProviderBase
  • ProviderCollection
  • ProviderException

Een eigen namespace is misschien te veel eer. Bovendien zitten andere relevante klassen in een andere namespace. De meeste Providers die standaard in .NET 2.0 geïmplementeerd zijn, hebben betrekking op ASP.NET. Vandaar dat er een ProvidersHelper klass in System.Web.Configuration zit. Dat neemt overigens niet weg dat wanneer je een eigen provider maakt die niets met ASP.NET te maken heeft je alsnog naar deze namespace kunt refereren.

De ProvidersHelper klasse is enkel bedoeld om, in de traditie van de Factory method, instanties te creëren van de gewenste Provider.

ProviderSettings

De ProviderSettings klasse vertegenwoordigt een <add /> element in de configuratie voor een specifieke Provider. Dit element ziet er bijvoorbeeld zo uit:

<add name="DefaultQuoteInformationProvider"

  type="YahooQuoteInformation.YahooQuoteInformationProvider, YahooQuoteInformation"

  description="Get the quotes from Yahoo."

  datetimeFormat="dd-MM-yyyy hh:mm"

 />

De Name en Type eigenschappen uit de ProviderSettings klasse refereren dus naar deze name en type attributen. De overige attributen worden geladen in een NameValueCollection onder de eigenschap ‘Parameters’.

Een eigen Provider

We hebben in vogelvlucht de belangrijkste elementen van het Provider model gezien. In het overzicht aan het eind van dit artikel wordt verwezen naar meer bronnen van informatie die buiten de scope van dit artikel vallen.  Het doel van het volgende voorbeeld is te leren welke elementen nodig zijn om een Provider gebaseerde feature te implementeren en te gebruiken. Het gaat dus niet zo ver als een volledige eigen implementatie van bijvoorbeeld de Membership of RoleProvider features van ASP.NET.

We maken gebruik van de onderstaande structuur. Een link naar de te downloaden bron vindt je onderaan dit artikel.

De voorbeeld feature die we willen bouwen is de mogelijkheid om prijsinformatie van aandelen op te halen. Hier zijn verschillende bronnen voor mogelijk (websites, webservices, databases). We willen gebruikers van onze ‘clientapplicatie’ in staat stellen om te switchen van de ene aanbieder van prijsinformatie naar de ander, zonder dat deze clientapplicatie harde verwijzingen heeft naar deze aanbieder. Een uitgelezen kans om deze feature te baseren op een Provider.

De basis definitie van de Provider is als volgt:

using System;

using System.Configuration.Provider;

 

namespace QuoteFeature

{

    public abstract class QuoteInformationProvider : ProviderBase

    {

        public abstract string DateTimeFormat { get; }

        public abstract QuoteInfo GetQuote(string symbol);

    }

}

Het doel van de provider is het kunnen ophalen van een prijs van een bepaald aandeel dat herkenbaar is via zijn symbol. Een symbol is de afkorting van het aandeel zoals deze gepubliceerd worden, zoals MSFT, GOOG, ABN. Je kunt op http://finance.yahoo.com/lookup symbols opzoeken. De prijsinformatie wordt vastgelegd in een object van het type QuoteInfo. Deze klasse kent properties als: Last, UpdateDateTime, Company, en Symbol.

Het opnemen van een DateTimeFormat eigenschap is wellicht niet zo zinvol. Meestal wil je op het allerlaatste moment het besluit nemen voor het datum-tijd formaat waarin een tijdstip moet worden weergegeven. Het opnemen van deze eigenschap in een Provider is enkel omwille van het voorbeeld gedaan.

Het is mogelijk om meerdere providers te kiezen. Het kan zijn dat we dezelfde provider met andere parameters willen gebruiken of een geheel andere provider. Dit wordt mogelijk gemaakt door een eigen ProviderCollection.

    public class QuoteInformationProviderCollection : ProviderCollection

    {

        public override void Add(ProviderBase provider)

        {

            if (provider == null)

                throw new ArgumentNullException("You need to supply a " +

                    "provider reference.");

            if (!(provider is QuoteInformationProvider))

                throw new ArgumentException("The supplied provider needs to " +

                    "derive from QuoteInformationProvider.");

            base.Add(provider);

        }

 

        new public QuoteInformationProvider this[string name]

        {

            get { return (QuoteInformationProvider)base[name]; }

        }

 

        public void CopyTo(QuoteInformationProvider[] array, int index)

        {

            base.CopyTo(array, index);

        }

    }

Je ziet dat het maken van een eigen ProvidersCollection voornamelijk generieke code is. De indexer (this[]) en de CopyTo methode geven simpelweg de gerefereerde instantie van de Provider terug.

De feature-klasse zorgt er voor dat de ontwikkelaar geen specifieke kennis van de Provider nodig heeft om de gewenste functionaliteit aan de gebruiker te leveren. De API moet daarvoor natuurlijk wel voldoende functionaliteit bevatten. Zoals eerder is aangegeven, is de feature-klasse een voorbeeld van het Façade design pattern.

public static class QuoteInformation

    {

        public static QuoteInformationProvider defaultProvider;

        public static QuoteInformationProviderCollection providerCollection;

 

        public static QuoteInformationProvider Provider

        {

            get { return defaultProvider; }

           

        }

 

         public static QuoteInformationProviderCollection Providers

         {

             get { return providerCollection; }

         }

 

         public static QuoteInfo GetQuote(string symbol)

         {

             return Provider.GetQuote(symbol);

         }

De default provider is aan te spreken via de Provider eigenschap. Als je meerdere Providers geconfigureerd hebt, kan je de specifieke provider benaderen via de Providers eigenschap. De feature-klasse biedt in hoofdzaak maar één relevante functie, namelijk het ophalen van Quote informatie. Je kunt je voorstellen dat features als Membership, Site Navigation, Health Monitoring een hele reeks van statische methoden kennen.

Een feature gebaseerd op een Provider kent drie levensfasen:

  1. Eerst is de feature niet geïnitialiseerd. Iedere aanroep van een methode uit de feature slingert de initialisatie aan.

  2. Als de initialisatie gelukt is, zijn de feature-methoden direct aanroepbaar; de feature is zogezegd geïnitialiseerd.

  3. Als de initialisatie mislukt kan de feature nog steeds geïnitialiseerd zijn, maar zich in een fout-status bevinden. De reden van de mislukte initialisatie moet wel ergens worden opgeslagen.

Het gevolg van deze fasen is dat we ofwel een volledig werkende statische klasse tot onze beschikking hebben, ofwel informatie over het mislukken van de initialisatie. In het voorbeeld zetten we een eventuele fout, in de vorm van een exceptie, in een eigenschap van de feature-klasse. Door deze eigenschap te onderzoeken kunnen we erachter komen waarom de initialisatie niet lukte. We voorkomen op deze manier ook dat de hele initialisatie telkens opnieuw wordt geprobeerd zodra in de applicatie een feature-methode wordt aangeroepen.

         // initialization related variables and logic

         private static bool isInitialized = false;

         private static Exception initializationException;

 

         private static object inializationLock = new object();

 

         static QuoteInformation()

         {  

             Initialize();

         }

De feature-klasse houdt haar status vast in de twee private variabelen: isInitialized en initializationException. De volledige implementatievan de initialisatie methode staat hieronder. Deze code is opnieuw tamelijk generiek van opzet. Wanneer je boeken of artikelen over het Provider model leest zal je de onderstaande code geregeld terugzien.

   private static void Initialize()

   {

    if (isInitialized) {

     if (initializationException == null) {

      return;

     }

     else {

      throw initializationException;

     }

    }

    // start initialization

    lock (inializationLock) {

     if (isInitialized) {

      if (initializationException == null) {

       return;

      }

      else {

       throw initializationException;

      }

     }

     try {

      QuoteInformationConfigurationSection cs =

       (QuoteInformationConfigurationSection)

       ConfigurationManager.GetSection("quoteInformation");

      if (cs.DefaultProvider == null || cs.Providers == null ||

       cs.Providers.Count == 0)

       throw new ProviderException("The feature needs a default provider " +

        "and at least one provider definition.");

      // instantiate the feature's provider

      providerCollection = new QuoteInformationProviderCollection();

      ProvidersHelper.InstantiateProviders(

       cs.Providers,

       providerCollection,

       typeof(QuoteInformationProvider));

 

      providerCollection.SetReadOnly();

 

      defaultProvider = providerCollection[cs.DefaultProvider];

     

      if (defaultProvider == null) {

       throw new ConfigurationErrorsException(

        "The default feature provider was not specified.",

        cs.ElementInformation.Properties["defaultProvider"].Source,

        cs.ElementInformation.Properties["defaultProvider"].LineNumber);

      }

     }

     catch (Exception ex) {

      initializationException = ex;

      isInitialized = true;

      throw ex;

     }

     isInitialized = true;

    }

   }

Zo vroeg mogelijk in de methode wordt gecontroleerd of er niet al een poging tot initialisatie geweest is. Als dat het geval is hoeven we dus niet verder te gaan. De statische feature-klasse zal dan immers een default provider en een providers-collectie bevatten (op zijn minst bestaande uit één provider). Tenzij de initalisatie is mislukt, zodat er een exceptie is vastgelegd. Dezelfde exceptie wordt overigens direct ook teruggegeven aan de aanroepende code. Wil je dus voorkomen dat een mislukte aanroep van een feature herhaald wordt, dan kan je simpelweg de initializationException eigenschap bekijken.

Als de methode merkt dat initialisatie nog niet heeft plaatsgevonden, gaat deze een synchronisatie block in met behulp van het lock statement. Voor de zekerheid wordt hier nog eens gecontroleerd op de status van de feature-klasse. In theorie kan het namelijk zijn dat twee threads tegelijkertijd een statische methode aanroepen. Zo kan de ene thread net klaar zijn met initialiseren maar de isInitialized variable nog net niet op true gezet hebben.

De kern van de initialisatie zit in de creatie van de provider met de ProvidersHelper klasse en het vullen van de providerCollection.

Provider implementatie

We hebben nu een abstracte klasse die de basis is voor onze provider implementatie en een feature-klasse om het aanroepen van providers te vergemakkelijken. We missen nog een feitelijke implementatie van de provider en de benodigde configuratie. Een Provider die prijsinformatie over aandelen ophaalt ziet er ongeveer zo uit:

using System;

using System.Configuration;

using System.Configuration.Provider;

 

namespace YahooQuoteInformation

{

    public class YahooQuoteInformationProvider : QuoteFeature.QuoteInformationProvider

    {

        private string datetimeFormat;

        public override string DateTimeFormat

        {

            get { return datetimeFormat; }

        }

 

        public override void Initialize(string name,

            System.Collections.Specialized.NameValueCollection config) {

           

        }

 

        public override QuoteFeature.QuoteInfo GetQuote(string symbol) {

           

            return Quote;

        }

 

        public override string Description {

            get {

                return base.Description;

            }

        }

 

        public override string Name {

            get {

                return base.Name;

            }

        }

    }

}

De klasse overschrijft (override) de methoden en eigenschappen uit de abstracte klasse. Ook hier hebben we te maken met een initialisatie methode die er enkel voor zorgt dat alle Provider implementatie specifieke parameters worden opgehaald. De config parameter bevat daarvoor de naam/waarde combinaties die zijn ingevuld in de configuratie.

De GetQuote methode is de specifieke implementatie om prijsgegevens op te halen. In dit voorbeeld gebruiken we Yahoo als bron voor onze informatie.

  public override QuoteFeature.QuoteInfo GetQuote(string symbol) {

 

   QuoteFeature.QuoteInfo Quote = new QuoteFeature.QuoteInfo();

   string request =

    string.Format("http://finance.yahoo.com/d/quotes.csv?s={0}&f=sl1d1t1c1ohgvn&e=.csv",

    symbol);

   HttpWebRequest webreq = (HttpWebRequest)WebRequest.Create(request);

   HttpWebResponse webresp = (HttpWebResponse)webreq.GetResponse();

   StreamReader strm = new StreamReader(webresp.GetResponseStream(), Encoding.ASCII);

   string line = strm.ReadLine().Replace("\"", "");

   Quote.UpdateDateTime = Convert.ToDateTime(line.Split(',')[2] + ' ' +

    line.Split(',')[3]).ToString(datetimeFormat);

   Quote.Symbol = line.Split(',')[0].Trim('"');

   Quote.Company = line.Split(',')[9].Trim('"');

   Quote.Last = Convert.ToDouble(line.Split(',')[1]);

   strm.Close();

   return Quote;

  }

In het kader van dit artikel doet deze specifieke methode natuurlijk niet ter zake. Kortgezegd komt het er op neer dat via de betreffende webpagina een regel wordt opgehaald waarvan specifieke elementen in het Quote object worden geplaatst.

Configuratie

De configuratie in de client applicatie zorgt voor de koppeling tussen de generieke functionaliteit en de specifieke implementatie. Deze configuratie ziet er als volgt uit:

  <configSections>

    <section name="quoteInformation"

             type="QuoteFeature.QuoteInformationConfigurationSection, QuoteFeature"

             allowDefinition="MachineToApplication"/>

  </configSections>

Deze informatie, vastgelegd in het App.Config (of Web.Config) bestand van de client applicatie, wordt gelezen door een implementatie van een ConfigurationSection klasse. Ook dit is weer generieke code:

using System;

using System.Configuration;

 

namespace QuoteFeature

{

    class QuoteInformationConfigurationSection : ConfigurationSection

    {

        public QuoteInformationConfigurationSection() { }

 

        [ConfigurationProperty("providers")]

        public ProviderSettingsCollection Providers

        {

            get { return (ProviderSettingsCollection)base["providers"]; }

        }

 

        [ConfigurationProperty("defaultProvider",

            DefaultValue = "DefaultQuoteInformationProvider")]

        [StringValidator(MinLength = 1)]

        public string DefaultProvider

        {

            get { return (string)base["defaultProvider"]; }

            set { base["defaultProvider"] = value; }

        }

    }

}

Door te erven van ConfigurationSection weten de applicatie dat het om een sectie in de configratie gaat. Het .NET framework leest deze secties automatisch en de enige extra code die je moet schrijven betreffen de specifieke eigenschappen die van belang zijn voor het vaststellen van je Provider.

Wanneer via de <configSections> is vastgesteld welke provider(s) gebruikt worden, worden de opgegeven secties eveneens ingelezen en aangeboden aan de Initialize methode van de specifieke Provider implementatie die we hierboven hebben gezien. Deze sectie ziet er bijvoorbeeld zo uit:

<quoteInformation defaultProvider="DefaultQuoteInformationProvider">

  <providers>

   <add name="DefaultQuoteInformationProvider"

      type="YahooQuoteInformation.YahooQuoteInformationProvider, YahooQuoteInformation"

      description="Get the quotes from Yahoo."

      datetimeFormat="dd-MM-yyyy HH:mm"

     />

   <add name="SecondQuoteInformationProvider"

      type="YahooQuoteInformation.YahooQuoteInformationProvider, YahooQuoteInformation"

      description="Get the quotes from Yahoo."

      datetimeFormat="yyyy-MM-dd HH:mm:ss"

     />

  </providers>

</quoteInformation>

We stellen vast dat er twee providers tot onze beschikking staan. Ze zijn in dit geval van hetzelfde type, maar zouden net zo goed twee verschillende kunnen zijn, zolang ze maar erven van dezelfde abstracte klasse QuoteInformationProvider.

Testen

De client bestaat uit weinig meer dan een console applicatie die met behulp van de configuratie de volgende code succesvol kan uitvoeren:

QuoteFeature.QuoteInfo Quote =

QuoteFeature.QuoteInformation.GetQuote("MSFT");

System.Console.WriteLine("quote from: {0}", Quote.Company);

System.Console.WriteLine("last      : {0}", Quote.Last);

System.Console.WriteLine("date      : {0}", Quote.UpdateDateTime);

System.Console.ReadLine();

Zodra GetQuote() uit de statische klasse QuoteInformation wordt aangeroepen initialiseert deze klasse de geselecteerde QuoteInformationProvider, in dit geval de opgegeven default Provider. Om een specifieke Provider te gebruiken, kan worden volstaan met:

QuoteFeature.QuoteInformation.Providers["SecondQuoteInformationProvider"].GetQuote("MSFT");

Conclusies

Het Provider model is een uitstekende manier om losgekoppelde applicaties te ontwerpen en te bouwen. Microsoft heeft met de introductie van dit model vooral ASP.NET erg flexibel gemaakt. Zo werkt de Membership feature in ASP.NET volledig via dit model waarmee het mogelijk wordt om gebruik te maken van SQL Server en Active Directory voor de de opslag van gebruikersgegevens, maar even zo makkelijk gekozen kan worden voor Access, MySQL of Oracle. Ook hiervan zijn Membership Providers beschikbaar en volstaan ze niet, dan kan met niet al te veel moeite een Provider geschreven worden met een eigen specifieke implementatie.

Ontwikkelaars van applicaties die klanten configurabele opties moeten bieden zijn eveneens gebaat bij het Provider model. Het is geenszins een revolutionaire technologie, maar het .NET Framework en zeker versie 2.0 maakt de toepassing ervan een stuk eenvoudiger.

Broncode

De broncode biedt de genoeg aanknopingspunten om aan de hand hiervan een eigen Provider gebaseerde feature te ontwikkelen. De oplettende ontwikkelaar zal merken dat in de client applicatie een referentie is opgenomen naar de specifieke implementatie YahooQuoteInformation. Dit is niet vanwege een harde relatie tussen de client en deze assembly, maar om er voor te zorgen dat de client bij de uitvoering de betreffende assembly wel kan vinden. Alternatieven zijn natuurlijk registratie in de global assembly cache of handmatig kopiëren naar de bin-folder waar de client applicatie staat. Dat maakt het snel kunnen uitvoeren van het voorbeeld evenwel wat moeilijker. Download de broncode hier.

Op deze pagina is ook een code template te vinden met daarin vijf voorbeelden voor het ontwikkelen van je eigen Provider gebaseerde feature.

Meer informatie

posted on Sunday, February 26, 2006 8:29 PM

Feedback

No comments posted yet.

Post Feedback

Title:
Name:
Url:
Comments: 
Protected by Clearscreen.SharpHIPEnter the code you see: