Freigeben über


Erweitern der objektrelationalen Zuordnung

Letzte Änderung: Freitag, 12. März 2010

Gilt für: SharePoint Foundation 2010

Die vom LINQ to SharePoint-Anbieter bereitgestellte objektrelationale Zuordnung deckt nicht jedes mögliche Szenario ab, in dem Sie möglicherweise LINQ-Zugriff auf eine Microsoft SharePoint Foundation-Inhaltsdatenbank in der Geschäftslogik benötigen. Es folgen Situationen, in denen Sie ggf. die Zuordnung erweitern müssen.

  • SPMetal kann nur Code für die Felder eines Inhaltstyps generieren. Es gibt keine vordefinierte Zuordnung zu den Eigenschaften von SPListItem-Objekten, z. B. zum Properties-Eigenschaftenbehälter oder zur Attachments-Eigenschaft.

  • Mit SPMetal kann kein Code für Felder generiert werden, die einen benutzerdefinierten Felddatentyp verwenden.

  • SPMetal kann nicht "die Zukunft vorhersagen", d. h. es kann keine Felder (Spalten) zuordnen, die die Benutzer den Listen nach der Bereitstellung Ihrer Lösung hinzufügen werden.

  • Sie können SPMetal ggf. nicht immer erneut ausführen, wenn sich der Entwurf einer Zielliste geändert hat und ihr neue Felder hinzugefügt wurden, da beispielsweise andere Entwicklungsteams den von SPMetal generierten Code gerade bearbeiten.

Aus diesen Gründen wird die ICustomMapping-Schnittstelle bereitgestellt, mit der Sie die objektrelationale Zuordnung erweitern können.

Erweitern einer LINQ to SharePoint-Lösung

Im Folgenden werden die grundlegenden Aufgaben für die Erweiterung der objektrelationalen Zuordnung beschrieben.

  • Abrufen der SPListItem-Eigenschaften oder neuer Spalten, die neuen Eigenschaften der Inhaltstypklasse zugeordnet sind

  • Behandeln von Parallelitätskonflikten im Zusammenhang mit den neuen Spalten

Diese Aufgaben werden durch die von LINQ to SharePoint bereitgestellte ICustomMapping-Schnittstelle erleichtert. Eine Klasse, die diese Schnittstelle implementiert, kann neue Spalten neuen Eigenschaften zuordnen. Sie kann außerdem das System zur Auflösung von Parallelitätskonflikten erweitern, sodass die neuen Spalten berücksichtigt werden.

Zuordnen von Spalten, für die benutzerdefinierte Feldtypen verwendet werden

Sie beginnen mit Ihrer Erweiterungscodedatei, indem Sie die Klasse, die den Inhaltstyp der Liste mit den neuen Spalten darstellt, erneut deklarieren. Die Klasse muss als partial (in Visual Basic Partial) markiert sein. (Sie muss auch in der ursprünglichen Codedatei, die in der Regel mithilfe von SPMetal generiert wird, als partial deklariert worden sein. Das SPMetal-Tool, dessen Verwendung empfohlen wird, deklariert alle von ihm generierten Inhaltstypklassen automatisch als partial.) Wiederholen Sie nicht die Attributdekorationen anhand der ursprünglichen Deklaration, sondern geben Sie an, dass die Klasse die ICustomMapping-Schnittstelle implementiert.

Deklarieren Sie innerhalb der Klasse die drei Methoden der Schnittstelle. Im folgenden Codebeispiel heißt der Inhaltstyp Book.

public partial class Book : ICustomMapping
{
    public void MapFrom(object listItem)
    {
    }

    public void MapTo(object listItem)
    {
    }

    public void Resolve(RefreshMode mode, object originalListItem, object databaseObject)
    {
    }

    // New property declarations go here.

}

Statten Sie die MapFrom(Object)-Methode mit einem CustomMappingAttribute-Attribut aus. Konstruieren Sie innerhalb dieses Attributs ein Array von Strings, die die internen Namen der Spalten enthalten, und ordnen Sie das Array der Columns-Eigenschaft zu.

HinweisHinweis

Der interne Name einer Spalte kann nicht in der Benutzeroberfläche von SharePoint Foundation bezogen werden. Sie müssen ihn mithilfe der InternalName-Eigenschaft über das Objektmodell abrufen.

Im folgenden Beispiel wird gezeigt, wie mit dem CustomMappingAttribute ein Array von internen Namen für zwei neue Spalten erstellt wird, die einer Liste Books hinzugefügt wurden.

  • ISBN ist eine Spalte, die einen benutzerdefinierten Felddatenttyp (ISBNData) für die ISBN-Nummer eines Buchs verwendet.

  • UPC-A ist eine Spalte mit einem benutzerdefinierten Feldtyp (UPCAData), der für strukturierte Daten vorgesehen ist, die zum Generieren eines UPC-A-Strichcodes für ein Buch verwendet werden können. Ihr interner Name ist "UPCA".

[CustomMapping(Columns = new String[] { "ISBN", "UPCA" })]
public void MapFrom(object listItem)
{
}

Die MapFrom(Object)-Methode wird von LINQ to SharePoint zum Festlegen von Eigenschaftswerten aus der Inhaltsdatenbank verwendet. Der an sie übergebene Parameter ist ein Objekt, das ein aus der Inhaltsdatenbank abgerufenes Listenelement darstellt. Die Methode muss für jede neue Spalte eine Zeile enthalten, die den Feldwert der Spalte derjenigen Eigenschaft zuordnet, anhand der Sie die Spalte in Ihrer Erweiterung der objektrelationalen Zuordnung darstellen möchten.

Mithilfe der MapTo(Object)-Methode wird ein Wert im entsprechenden Feld der Inhaltsdatenbank gespeichert. Der an sie übergebene Parameter ist ein Objekt, das ein aus der Inhaltsdatenbank abgerufenes Listenelement darstellt. Die Methode muss für jede Eigenschaft, die eine neue Spalte darstellt, eine Zeile enthalten, die den Wert der Eigenschaft dem Feld der Spalte in der Datenbank zuordnet.

Wichtiger HinweisWichtig

Rufen Sie die MapFrom(Object)- oder die MapTo(Object)-Methode nicht aus dem eigenen Code auf.

Im folgenden Beispiel wird die Implementierung dieser Methoden veranschaulicht.

[CustomMapping(Columns = new String[] { "ISBN", "UPCA" })]
public void MapFrom(object listItem)
{
    SPListItem item = (SPListItem)listItem;
    this.ISBN = item["ISBN"];
    this.UPCA = item["UPCA"];
}

public void MapTo(object listItem)
{
    SPListItem item = (SPListItem)listItem;
    item["ISBN"] = this.ISBN;
    item["UPCA"] = this.UPCA;
}

Sie können jeder Methode weitere Logik hinzufügen, die Sie ggf. benötigen, z. B. Überprüfungslogik.

Zuordnen von "SPListItem"-Eigenschaften

Generell müssen Sie die objektrelationale Zuordnung erweitern, wenn Sie im LINQ to SharePoint-Code auf Eigenschaften von SPListItem-Objekten zugreifen müssen, die nicht die Felder des Inhaltstyps des Elements sind. In diesem Szenario übergeben Sie die Zeichenfolge "*" als einzigen Member der Columns-Eigenschaft von CustomMappingAttribute. Die Logik der MapFrom(Object)- und der MapTo(Object)-Methode ordnet die SPListItem-Eigenschaft einfach einer entsprechenden Eigenschaft Ihrer Inhaltstypklasse zu. Das Schreiben des aufrufenden Codes ist wahrscheinlich einfacher, wenn Sie für die Eigenschaft in Ihrer Klasse den gleichen Namen verwenden wie für die entsprechende Eigenschaft in der SPListItem-Klasse. Im Folgenden ein Beispiel dazu.

[CustomMapping(Columns = new String[] { "*" })]
public void MapFrom(object listItem)
{
    SPListItem item = (SPListItem)listItem;
    this.File = item.File;
}

public void MapTo(object listItem)
{
    SPListItem item = (SPListItem)listItem;
    item.File = this.File;
}

Mit der "*"-Zeichenfolge wird LINQ to SharePoint angewiesen, das gesamte SPListItem-Objekt aus der Datenbank, alle Eigenschaften sowie alle Felder abzurufen. Es gibt eine Eigenschaft, für die dies nicht notwendig ist. Wenn Attachments die einzige Eigenschaft von SPListItem ist, die Sie zuordnen möchten, können Sie "Attachments" als Zeichenfolge im Columns-Array verwenden, dann ruft LINQ to SharePoint nicht nur das boolesche Feld (die Spalte) "Attachments" ab, sondern auch die Attachments-Eigenschaft.

Zuordnen von Spalten, die Benutzer nach der Bereitstellung hinzufügen

Wenn Sie "*" als einziges Element im Columns-Array verwenden, können Sie auch Spalten zuordnen, die die Benutzer der Liste hinzufügen, nachdem Ihre Lösung den Membern einer Dictionary<TKey, TValue>-Eigenschaft in Ihrer Inhaltstypklasse bereitgestellt worden ist (wobei TKey String ist und TValue Object ist). Dazu ordnen Sie den internen Namen jedes Felds einem Wörterbucheintrag mit einem gleichnamigen Schlüssel zu. Im Folgenden ein Beispiel dazu.

[CustomMapping(Columns = new String[] { "*" })]
public void MapFrom(object listItem)
{
    SPListItem item = (SPListItem)listItem;
    foreach (var field in item.Fields)
    {
        this.Properties[field.InternalName] = item[field.InternalName];
    }
}

public void MapTo(object listItem)
{
    SPListItem item = (SPListItem)listItem;
    foreach (var kvp in this.Properties)
    {
        item[kvp.Key] = this.Properties[kvp.Key];
    }
}

Zuordnen des Eigenschaftenbehälters des Listenelements

Sie können mithilfe der ICustomMapping-Methoden auch Eigenschaften einzelnen Hashtabelleneinträgen innerhalb desjenigen Datenbankfelds zuordnen, das der SPListItem.Properties-Eigenschaft entspricht. In diesem Szenario deklarieren Sie in der Inhaltstypklasse keine Eigenschaft für einen Eigenschaftenbehälter. Vielmehr deklarieren Sie eine separate Eigenschaft für jede Eigenschaft im Eigenschaftenbehälter des Listenelements, den Sie zuordnen möchten. Implementieren Sie die Methoden von ICustomMapping, wie im folgenden Beispiel gezeigt.

[CustomMapping(Columns = new String[] { "*" })]
public void MapFrom(object listItem)
{
    this.PreviousManager = ((SPListItem)listItem).Properties["PreviousManager"];
}

public void MapTo(object listItem)
{
    ((SPListItem)listItem).Properties["PreviousManager"] = this.PreviousManager;
}

Verarbeiten von Parallelitätskonflikten bei den neuen Spalten

Damit sichergestellt ist, dass die Eigenschaften in das System für die Objektänderungsverfolgung einbezogen werden, muss der set-Akzessor der Eigenschaften die OnPropertyChanging- und die OnPropertyChanged-Methode der Inhaltstypklasse aufrufen, wie im folgenden Beispiel gezeigt. Diese Methoden sind Teil des von SPMetal generierten Codes. Sie verarbeiten jeweils das PropertyChanging- und das PropertyChanged-Ereignis. Im Folgenden ein Beispiel für eine der weiter oben in diesem Thema besprochenen Spalten, für die ein benutzerdefinierter Feldtyp verwendet wird. Beachten Sie, dass der benutzerdefinierte Feldtyp ISBNData ist.

private ISBNData iSBN;

public ISBNData ISBN 
{
    get 
    {
        return iSBN;
    }
    set 
    {
        if ((value != iSBN)) 
        {
            this.OnPropertyChanging("ISBN", iSBN);
            iSBN = value;
            this.OnPropertyChanged("ISBN");
        }
    }
}
HinweisHinweis

Verwenden Sie kein ColumnAttribute für die Eigenschaftendeklaration.

Implementieren Sie die Resolve(RefreshMode, Object, Object)-Methode, um die neuen Spalten in den Prozess für die Auflösung von Parallelitätskonflikten einzutragen. Die ObjectChangeConflict.Resolve()- und die MemberChangeConflict.Resolve()-Methode überprüfen jedes Listenelement dahin gehend, ob der jeweilige Inhaltstyp ICustomMapping implementiert. Für die Listenelemente, bei denen dies zutrifft, ruft jede dieser Methoden die ICustomMapping.Resolve(RefreshMode, Object, Object)-Methode auf. Der RefreshMode-Wert, der an ICustomMapping.Resolve(RefreshMode, Object, Object) übergeben wird, ist der gleiche Wert, der auch an die aufrufende Methode übergeben wurde.

Im Folgenden ein Beispiel für die Implementierung von ICustomMapping.Resolve(RefreshMode, Object, Object).

Wichtiger HinweisWichtig

Die Implementierung schreibt nur in die Eigenschaften, die die neuen Spalten darstellen, die durch die ICustomMapping-Methoden zugeordnet werden. Bei Ihrer Implementierung müssen Sie die gleiche Vorgehensweise einhalten. Die alten Eigenschaften sind zum Zeitpunkt des Aufrufs von ICustomMapping.Resolve(RefreshMode, Object, Object) bereits durch die ObjectChangeConflict.Resolve()- oder die MemberChangeConflict.Resolve()-Methode aufgelöst worden. Es besteht die Gefahr, dass das Ergebnis aus den beiden vorherigen Methoden wieder zunichtegemacht wird, wenn Ihre Implementierung in eine der alten Eigenschaften schreibt.

public void Resolve(RefreshMode mode, object originalListItem, object databaseListItem)
{
    SPListItem originalItem = (SPListItem)originalListItem;
    SPListItem databaseItem = (SPListItem)databaseListItem;

    ISBNData originalISBNValue = (ISBNData)originalItem["ISBN"];
    ISBNData dbISBNValue = (ISBNData)databaseItem["ISBN"];

    UPCAData originalUPCAValue = (UPCAData)originalItem["UPCA"];
    UPCAData dbUPCAValue = (UPCAData)databaseItem["UPCA"];

    if (mode == RefreshMode.OverwriteCurrentValues)
    {
        this.ISBN = dbISBNValue;
        this.UPCA = dbUPCAValue;
    }
    else if (mode == RefreshMode.KeepCurrentValues)
    {
        databaseItem["ISBN"] = this.ISBN;
        databaseItem["UPCA"] = this.UPCA;        
    }
    else if (mode == RefreshMode.KeepChanges)
    {
        if (this.ISBN != originalISBNValue)
        {
            databaseItem["ISBN"] = this.ISBN;
        }
        else if (this.ISBN == originalISBNValue && this.ISBN != dbISBNValue)
        {
            this.ISBN = dbISBNValue;
        }

        if (this.UPCA != originalUPCAValue)
        {
            databaseItem["UPCA"] = this.UPCA;
        }
        else if (this.UPCA == originalUPCAValue && this.UPCA != dbUPCAValue)
        {
            this.UPCA = dbUPCAValue;
        }
    }
} 

In diesem Beispiel wird die Implementierung von Resolve für das Szenario der Zuordnung des Eigenschaftenbehälters des Listenelements gezeigt.

public void Resolve(RefreshMode mode, object originalListItem, object databaseListItem)
{
    SPListItem originalItem = (SPListItem)originalListItem;
    SPListItem databaseItem = (SPListItem)databaseListItem;

    string originalPreviousManagerValue = 
                originalItem.Properties["PreviousManager"].ToString();
    string dbPreviousManagerValue = 
                databaseItem.Properties["PreviousManager"].ToString();
    
    if (mode == RefreshMode.OverwriteCurrentValues)
    {
        this.PreviousManager = dbPreviousManagerValue;
    }
    else if (mode == RefreshMode.KeepCurrentValues)
    {
        databaseItem.Properties["PreviousManager"] = this.PreviousManager;
    }
    else if (mode == RefreshMode.KeepChanges)
    {
        if (this.PreviousManager != originalISBNValue)
        {
            databaseItem.Properties["PreviousManager"] = this.PreviousManager;
        }
        else if (this.PreviousManager == originalISBNValue && this.PreviousManager != dbPreviousManagerValue)
        {
            this.PreviousManager = dbPreviousManagerValue;
        }
    }      
}