Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs
ASP German Homepage Homepage
 

Liste

.NET 2.0 (1)
.NET Allgemein (16)
.NET Fu (5)
ADO.NET (11)
Aprilscherz (3)
ASP Grundlagen (44)
ASP Tricks (83)
ASP.NET (44)
ASPIntranet.de (5)
C# (28)
Datenbank (44)
Dokumentation (4)
IIS 6.0 (1)
Komponenten (29)
Optimierung (10)
Server (21)
Sicherheit (34)
Tee Off (6)
VB.NET (6)
WAP (8)
Web Services (11)
XML (9)

RSS 2.0 - Die neuesten fünf Artikel auf AspHeute.com


 

Suchen





 

English Articles
Chinese Articles
Unsere Autoren
 
Link zu AspHeute
Impressum
Werben
Anfragen

Index Server Abfragen per Web Service

Geschrieben von: Christoph Wille
Kategorie: Web Services

Wenn Google seinen Suchkatalog per Web Service anbietet, warum sollte man dann nicht seine lokale Sitesuche per Web Service den Programmierern öffnen? Heute werden wir einen solchen Suchservice basierend auf dem Index Server, der ja Teil von Windows 2000 ist, implementieren. Und damit man die Nützlichkeit eines solchen Services sieht, erstellen wir ihn für AspHeute.com, und geben auch noch einen Windows-Client mit dazu.

Es gibt bis dato keine managed Klassen um auf die Index Server Catalogs zugreifen zu können. Daher müssen wir den Weg mit COM InterOp wählen, und wie im Artikel Objektbasierte Index Server Suche die Index Server Search Objects (IXSSO) einsetzen. Damit erhalten wir die volle Suchfunktionalität von Index Server, die auch sehr gut in Integrating Other Server-Side Technologies beschrieben ist.

Erstellen des Web Services

Unser erster Schritt ist ein leeres Web Service Projekt in Visual Studio.NET zu erstellen (Hinweis: natürlich kann man diesen Web Service auch ohne VS.NET erstellen). Diesem fügen wir die COM Referenz für die Index Server Search Objects hinzu:

Wir erhalten einen RCW - einen Runtime Callable Wrapper (siehe auch Artikel Verwenden von COM Komponenten in ASP.NET). Diesen können wir im Object Browser betrachten:

Im Interface IixssoQuery sind die für uns vordringlich wichtigen Eigenschaften und Methoden beheimatet, so auch die CreateRecordset Methode, die die Query ausführt und die Resultate zurückliefert. Das interessante daran ist, daß die Resultate als ADO Recordset geliefert werden, wir also eine weitere COM Referenz in unserem Projekt benötigen:

Hierbei handelt es sich um eine sogenannte Primary Interoperability Assembly, das heißt, sie wird vom Hersteller (Microsoft in diesem Fall) geliefert, und ist im GAC abgelegt. Einige Internet Provider sperren den GAC jedoch, sodaß es notwendig sein kann, diese Assembly aus dem GAC der lokalen Maschine herauszukopieren und ins bin Verzeichnis der Website zu installieren (dazu mehr später).

Damit hätten wir alle Vorbereitungen abgeschlossen, und wir können an das Schreiben des Web Service Codes gehen:

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Web;
using System.Web.Services;

using System.Data;
using System.Data.OleDb;

namespace Suche
{
  [WebService(Namespace="http://microsoft.com/webservices/")]
  public class AspHeuteSearch : System.Web.Services.WebService
  {
    private static string CiCatalog = "aspheute.com";
    private static string CiScope = "/artikel/";
    private static string CiFlags = "SHALLOW";  // "DEEP" not needed in CiScope

    public AspHeuteSearch()
    {
      InitializeComponent();
    }

Zu den standardmäßigen using Statements hat sich System.Data.OleDb dazugesellt - den Grund sehen wir später im Code. Es folgt die Klasse AspHeuteSearch, und einige statische Variablen, die nicht veränderbare Werte halten, zB den Namen des Index Server Catalogs, das Suchverzeichnis und die Suchtiefe. Mit diesen Definitionen können wir daran gehen, unsere beiden Suchmethoden zu implementieren:

    [WebMethod]
    public DataSet KeywordSearch(string Keyword, int MaxResults)
    {
      if (MaxResults < 5 || MaxResults > 250)
        throw new ArgumentOutOfRangeException("MaxResults", MaxResults, 
			"Range is 5-250");

      if (Keyword.Length < 3)
        throw new ArgumentOutOfRangeException("Keyword", Keyword, 
			"Minimum length of search keyword is 3 characters");

      return PerformQuery("@ALL " + Keyword, MaxResults);
    }
    
    [WebMethod]
    public DataSet IXAdvancedQuery(string Query, int MaxResults)
    {
      if (MaxResults < 5 || MaxResults > 250)
        throw new ArgumentOutOfRangeException("MaxResults", MaxResults, 
			"Range is 5-250");

      return PerformQuery(Query, MaxResults);
    }

Einerseits ist das KeywordSearch und andererseits IXAdvancedQuery. Der Unterschied ist, daß erstere eine einfache Schlüsselwortsuche erlaubt, wohingegen die zweite die gesamte Suchsyntax des Index Servers bis hin zu Regular Expressions erlaubt, der Expertenmodus sozusagen. Beide Methoden checken die übergebenen Parameter, und werfen nötigenfalls Exceptions falls der Input nicht den Erwartungen entspricht ("All input is evil until proven otherwise"). Gemeinsam ist beiden der Aufruf von PerformQuery:

    private DataSet PerformQuery(string strQuery, int nMaxResults)
    {
      Cisso.CissoQueryClass cqc = new Cisso.CissoQueryClass();
      cqc.Catalog = CiCatalog;
      cqc.MaxRecords = nMaxResults;
      cqc.CiScope = CiScope;
      cqc.CiFlags = CiFlags; 
      cqc.Query = strQuery;
      cqc.Columns = "Vpath,DocTitle,Characterization,Rank";
      cqc.SortBy = "Rank[d]";

      ADODB.Recordset rsIX = (ADODB.Recordset)cqc.CreateRecordset("nonsequential");

      OleDbDataAdapter daConvertToDataset = new OleDbDataAdapter();
      DataSet myDS = new DataSet();
      daConvertToDataset.Fill(myDS, rsIX, "IXResults");
      return myDS;
    }

Diese Methode ist das "Arbeitstier" unseres Webservices. Es erstellt ein Objekt vom Typ CissoQueryClass, und weist alle Eigenschaften zu, die man für die Ausführung der CreateRecordset Methode benötigt. In dieser Methode wird auch der Grund ersichtlich, warum wir den System.Data.OleDb Namespace eingebunden haben - der OleDbAdapter bietet eine performante Möglichkeit, ein ADO Recordset in ein ADO.NET DataSet umzuwandeln. Und dieses DataSet wird zum Client geschickt, der es dann weiterverarbeiten kann.

Bevor wir soweit sind, möchte ich noch das eine oder andere Wort über das Deployment dieses Web Services verlieren. Abgesicherte Web Server eines Providers unterscheiden sich von den "weltoffenen" Entwicklungsmaschinen, die VS.NET installiert haben. Deshalb muß man die web.config seinen Bedürfnissen anpassen:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
  <system.web>
    <compilation 
         defaultLanguage="c#"
         debug="true">

     <assemblies>
        <add assembly="Suche" />
        <add assembly="InterOp.Cisso" />
	<add assembly="ADODB"/>
     </assemblies>

    </compilation>

    <customErrors mode="Off" /> 


    <trace
        enabled="false"
        requestLimit="10"
        pageOutput="false"
        traceMode="SortByTime"
		localOnly="true"
    />

 </system.web>

</configuration>

Der wichtige Punkt hier ist der Abschnitt assemblies - hier definiert man, welche Assemblies in dieser Web Applikation geladen werden sollen. Klarerweise benötigt man die Assembly des Web Services. Ebenso wichtig sind aber auch die Assemblies für COM InterOp von IXSSO und ADODB. Die Assembly adodb.dll kann man aus dem GAC kopieren, sie ist im Download des Web Services im deploy Folder mit dabei. Damit ist der Web Service lauffähig und kann verwendet werden.

Aufrufen des Web Services

Den ersten Test kann man natürlich online im Web Browser durchführen, indem man die Adresse

http://www.aspheute.com/suche/aspheutesearch.asmx

eintippt. Damit gelangt man zur autogenerierten Seite des Webservices und kann beide Suchmethoden einmal durchtesten:

Interessanter ist natürlich ein vollständiger Client, der einige nette Features bieten kann:

Dieses Programm stellt Suchergebnisse in einer Listview dar. Klickt der Anwender auf einen Artikel, wird das Abstract angezeigt. Ist der Artikel interessant für den Benutzer, klickt er auf "View Article!", und das Programm lädt den Artikel im Internet Explorer (man könnte dies natürlich in einer weiteren Ausbaustufe in die Applikation integrieren).

Der erste Schritt bei der Erstellung einer Clientapplikation ist es, den Web Service zu referenzieren, und zwar mittels Add Web Refernce:

Das Erstellen des User Interfaces wird nicht gezeigt, interessant hingegen ist der Code der ausgeführt wird, wenn der Benutzer auf den "Search!" Button in der Applikation klickt:

    private void btnSearch_Click(object sender, System.EventArgs e)
    {
      string strKeyword = txtKeyword.Text;
      AspHeute.AspHeuteSearch ahs = new AspHeute.AspHeuteSearch();
      DataSet ds = ahs.KeywordSearch(strKeyword, 50);
      DataTable dt = ds.Tables[0];

      lvResults.BeginUpdate();
      txtUrl.Text = "";
      lvResults.Items.Clear();
      foreach (DataRow dr in dt.Rows)
      {
        ListViewItem lvi = new ListViewItem();

        // Vpath,DocTitle,Characterization,Rank -> order of columns in table
        lvi.Text = dr[1].ToString(); 
        lvi.SubItems.Add(dr[0].ToString());
        lvi.SubItems.Add(dr[2].ToString());
        lvResults.Items.Add(lvi);
      }
      lvResults.EndUpdate();

      MessageBox.Show("Search is complete - " + 
        dt.Rows.Count.ToString() + 
        " results returned from search",
        "Search AspHeute.com");
  }

Der Aufruf des Services sind nur zwei Zeilen (zugegebenermaßen fehlt Exception Handling). Der Rest des Codes befasst sich mit dem Befüllen der Listview basierend auf den Resultaten der Suche. Die Daten für URL und Abstract werden in unsichtbaren SubItems der Listview versteckt - ausgelesen werden diese im SelectedIndexChanged Event:

    private void lvResults_SelectedIndexChanged(object sender, System.EventArgs e)
    {
      if (lvResults.SelectedItems.Count == 0) return;
      ListViewItem lvi = lvResults.SelectedItems[0];
      txtSummary.Text = lvi.SubItems[2].Text;  // show summary text
      txtUrl.Text = lvi.SubItems[1].Text;      // define hyperlink of document
    }

Bei txtUrl handelt es sich um eine versteckte Textbox, die dann auch zum Anzeigen des Artikels im Internet Explorer verwendet wird:

    private void btnViewArticle_Click(object sender, System.EventArgs e)
    {
      if (txtUrl.Text != "")
        Process.Start("http://www.aspheute.com" + txtUrl.Text);
    }

Für Process.Start benötigt man noch den System.Diagnostics Namespace, aber das war dann schon der gesamte Code für den Windows-Client für unseren Web Service. Der Client ist natürlich ausbaufähig: ein asynchroner Aufruf des Web Services wäre wünschenswert, ein Paging durch die Resultate, ein integrierter Web Browser, etc.

Schlußbemerkung

Für den Web Service gibt es einen Kritikpunkt in punkto Interoperabilität: er liefert ein DataSet zurück, das an .NET gebunden ist. Hier sollte man auf ein Array von Result-Objekten umstellen, was nicht weiter schwierig ist - aber ein wenig Programmieraufwand bedeutet.

Ein kleiner Performancetip am Ende - die Index Server Search Objects (IXSSO) erlauben Paging mittels Querystrings - dies kann die Performance des ganzen Web Services deutlich verbessern. Der Web Service müßte nur dahingehend verändert werden, daß anstatt des MaxRecords Parameters ein String als "Bookmark" übergeben wird, der diesen Querystring emuliert.

Download des Codes

Klicken Sie hier, um den Download zu starten.

Verwandte Artikel

Das DataTable Objekt in ADO.NET
Das using Schlüsselwort
Datenbankzugriff mittels ADO.NET
Einstellungssache - Applikationsdaten in web.config
Exception Handling in C#
Objektbasierte Index Server Suche
Programmieren mit den Google Web APIs Beta 2
Verwenden von COM Komponenten in ASP.NET
Web Services 101 in ASP.NET
Web Services in Anwendungen konsumieren
Web.Config 101

Links zu anderen Sites

Integrating Other Server-Side Technologies

Wenn Sie jetzt Fragen haben...

Wenn Sie Fragen rund um die in diesem Artikel vorgestellte Technologie haben, dann schauen Sie einfach bei uns in den Community Foren der deutschen .NET Community vorbei. Die Teilnehmer helfen Ihnen gerne, wenn Sie sich zur im Artikel vorgestellten Technologie weiterbilden möchten.

Eine weitere sehr hilfreiche Resource ist das deutsche ASP.NET Wiki, das als zentrale Anlaufstelle für Tips, Tricks, Know How und alles Nützliche was man in seinem Alltag als (ASP).NET-Entwickler so braucht und entdeckt gedacht ist.

Haben Sie Fragen die sich direkt auf den Inhalt des Artikels beziehen, dann schreiben Sie dem Autor! Unsere Autoren freuen sich über Feedback zu ihren Artikeln. Ein einfacher Klick auf die Autor kontaktieren Schaltfläche (weiter unten) und schon haben Sie ein für diesen Artikel personalisiertes Anfrageformular.

 

Und zu guter Letzt möchten wir Sie bitten, den Artikel zu bewerten. Damit helfen Sie uns, die Qualität der Artikel zu verbessern - und anderen Lesern bei der Auswahl der Artikel, die sie lesen sollten.

Bewerten Sie diesen Artikel
 Sehr gut   Nicht genügend  
   1  2  3  4  5  
 

  
   Für Ausdruck optimierte Seite

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.