Entitätsklassen

Letzte Änderung: Dienstag, 9. März 2010

Gilt für: SharePoint Foundation 2010

Inhalt dieses Artikels
Verwenden von SPMetal
DataContext-Klasse
Listen, Inhaltstypen und Listenelemente

In diesem Thema werden die Entitätsklassen des LINQ to SharePoint-Anbieters vorgestellt. Diese Klassen stellen eine schlanke, objektrelationale Schnittstelle zwischen Ihrem objektorientierten C#- oder Microsoft Visual Basic-Code und der relationalen Tabellenstruktur der Inhaltsdatenbanken von Microsoft SharePoint Foundation bereit. Darüber hinaus stellen sie die Grundlage des Systems zur Nachverfolgung von Objektänderungen dar.

Die objektrelationale Schnittstelle ermöglicht objektorientierten Code in LINQ-Abfragen sowie das Erstellen, Löschen und Aktualisieren von Listenelementen mithilfe von objektorientiertem Code, der unabhängig vom regulären SharePoint Foundation-Objektmodell auf Datenbankentitäten verweist. Wenn das objektrelationale Framework einmal eingerichtet ist, können Sie es außerdem für objektorientierte Geschäftslogik in allen Situationen verwenden, in denen das kompakte und verzögerte Laden mithilfe des Frameworks Vorteile gegenüber dem regulären SharePoint Foundation-Objektmodell bietet. Durch die Verwendung der Entitätsklassen anstelle des regulären Objektmodells kann möglicherweise auch die Lesbarkeit von Code verbessert werden, wenn sich der Inhaltsverweis in der Nähe von LINQ-Abfragecode befindet, der dieselben Entitätsklassen verwendet. Beispielsweise in einer foreach-Schleife, die das Ergebnis einer LINQ-Abfrage wiederholt.

Verwenden von SPMetal

Das Erstellen der in diesem Artikel behandelten Klassen kann ziemlich mühsam und fehleranfällig sein. Deshalb gibt es in SharePoint Foundation das Befehlszeilentool SPMetal, mit dem Sie die Klassen- und Eigenschaftsdeklarationen generieren können. Es liest die Inhaltstypdefinitionen und Listenschemas für die Listen in einer SharePoint Foundation-Zielwebsite und generiert dann damit Entitätsklassen, einschließlich der Attributangaben. Dieses Tool ist im Lieferumfang von SharePoint Foundation enthalten und ist normalerweise im Ordner %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\BIN gespeichert. Es wird empfohlen, dieses Tool zu verwenden.

Die Art und Weise, wie von SPMetal Code generiert wird, können Sie anpassen. Da außerdem alle damit generierten Klassen als partial (Partial in Visual Basic) gekennzeichnet sind, können Sie ihnen Member in einer separaten Codedatei hinzufügen. Wenn Sie die Entitätsklassen mit SPMetal erneut generieren müssen, wird nur die generierte Datei überschrieben, nicht die Datei mit den benutzerdefinierten Membern. Weitere Informationen zum Verwenden von SPMetal finden Sie unter Vorgehensweise: Verwenden von SPMetal.

TippTipp

Es wird empfohlen, SPMetal zum Generieren aller Deklarationen der in diesem Artikel behandelten Klassen zu verwenden. Außerdem sollten die Codebeispiele in diesem Artikel in Ihren eigenen Code kopiert werden. Sie sind auch keine exakten Kopien dessen, was mit SPMetal generiert würde. Die Codebeispiele sind vereinfacht und Code wurde weggelassen, der für Demonstationszwecke belanglos ist. Insbesondere wurde der gesamte Code im Zusammenhang mit dem System zur Nachverfolgung von Objektänderungen in diesen Beispielen weggelassen. Weitere Informationen zu diesem System und dem unterstützten Code finden Sie unter Nachverfolgen von Objektänderungen und vollständige Parallelität. Darüber hinaus sind die Attributangaben in den Klassen- und Eigenschaftsdeklarationen in diesem Thema im Allgemeinen einfacher als in der Praxis, und einige Attribute wurden vollständig weggelassen. Weitere Informationen zu den Attributen und deren Verwendung finden Sie in den Referenzthemen für die folgenden Klassen (und deren Member):

DataContext-Klasse

Ganz oben in der Hierarchie der Entitätsklassen ist die DataContext-Klasse, die den Inhalt einer Website repräsentiert. Sie können ein DataContext-Objekt instanziieren, indem Sie an den Konstruktor die URL der Website übergeben, deren Daten Sie modellieren möchten. Rufen Sie anschließend die GetList<T>(String)-Methode auf, um einen Verweis auf eine bestimmte Liste abzurufen. Der folgende Code ist ein Beispiel dafür:

DataContext teamData = new DataContext("http://DeptServer/TeamSite")
EntityList<Announcment> teamAnns = teamData.GetList<Announcement>("Announcements");

Angenommen, Sie haben eine Klasse zum Repräsentieren des Inhaltstyps Announcement (siehe weiter unten) erstellt. Nun könnten Sie teamAnns abfragen. Es folgt ein Beispiel.

var excitingAnns = from ann in teamAnns
                   where ann.Title.EndsWith("!")
                   select ann;

Normalerweise ist der aufrufende Code lesbarer und objektorientierter, wenn Sie eine Klasse deklarieren, die von DataContext erbt und die eine Eigenschaft für jede Liste der Website aufweist. Es folgt ein Beispiel für solche Deklarationen.

public partial class TeamSite : DataContext 
{
    public TeamSite(string requestUrl) : 
        base(requestUrl) { }

    [List(Name="Announcements")]
    public EntityList<Announcement> Announcements 
    {
        get { return this.GetList<Announcement>("Announcements"); }
    }

}
HinweisHinweis

Die ListAttribute-Klasse legt fest, dass die Eigenschaft eine angegebene Liste in der Website repräsentiert, die durch das DataContext-Objekt repräsentiert wird.

Nachdem Sie diese Deklaration erstellt haben, ist für den aufrufenden Code kein expliziter Aufruf der GetList<T>(String)-Methode erforderlich, und außerdem ist er selbsterklärender.

TeamSite teamData = new TeamSite("http://DeptServer/TeamSite");

var excitingAnns = from ann in teamData.Announcements
                   where ann.Title.EndsWith("!")
                   select ann;
Wichtiger HinweisWichtig

Mit dem Konstruktor für die TeamSite-Klasse wird nicht die gesamte Liste Announcements geladen. Die EntityList<TEntity>-Klasse unterstützt das verzögerte Laden. Objekte dieses Typs werden erst geladen, wenn sie später im Code explizit angegeben werden.

Mit dem Tool SPMetal wird stets eine Klasse generiert, die von DataContext abgeleitet wird. Sie können konfigurieren, welche Listen damit modelliert werden. Standardmäßig wird eine Eigenschaft für jede nicht ausgeblendete Liste in der Website deklariert.

HinweisHinweis

Sie können natürlich Methoden, Ereignisse, Felder und Eigenschaften hinzufügen, die keine Listen für die von DataContext abgeleitete Klasse darstellt. Falls Sie solche Member hinzufügen, sollten die Deklarationen in einer separaten Codedatei getrennt von der von SPMetal generierten Datei vorhanden sein. Dadurch wird verhindert, dass der Code überschrieben wird, wenn Sie den von SPMetal erstellten Code erneut generieren müssen.

Listen, Inhaltstypen und Listenelemente

Jede Lösung, die den LINQ to SharePoint-Anbieter verwendet, muss Entitätsklassen zum Repräsentieren der SharePoint-Listen-, -Inhaltstypen und -Listenelemente verwenden.

Die EntityList<TEntity>-Klasse repräsentiert eine SharePoint Foundation-Liste. Die Typvariable, TEntity, ist der Typ der Listenelemente, d. h., der Inhaltstyp. Eine Eigenschaft vom Typ EntityList<TEntity> erhält den gleichen Namen wie die Liste, die dieser repräsentiert, wie z. B. Announcements oder Calendar. TEntity wird in der Regel mit der Singularform desselben Begriffs benannt, wie z. B. Announcement, aber wenn der Listenname selbst den Singular aufweist, kann ein anderer Begriff verwendet werden. Beispielsweise wird CalendarEvent als Typ für die Klasse verwendet, die eine Kalenderliste repräsentiert. Für eine benutzerdefinierte Liste wird der Typname durch Verketten des Listennamens und des Begriffs Item erstellt. Beispielsweise erhält die benutzerdefinierte Liste Books den Typ BooksItem. Die EntityList<TEntity>-Klasse ist geschützt und weist keinen öffentlichen Konstruktor auf. Im Abschnitt DataContext-Klasse finden Sie ein Beispiel, wie Sie für eine Eigenschaft den Typ EntityList<TEntity> für eine bestimmte TEntity-Variable deklarieren.

Die Inhaltstypen selbst müssen als Klassen deklariert werden. Die Deklaration wird wie im folgenden Beispiel veranschaulicht durch eine ContentTypeAttribute-Klasse (die auf ContentType verkürzt werden kann) ergänzt.

[ContentType(Name="Announcement" Id="Ox0104")]
public partial class Announcement
{

}

Jedes Feld (und jede Metadateneigenschaft) einer Liste muss mit einer öffentlichen Eigenschaft in der Inhaltstyp-Klassendefinition repräsentiert werden, wenn Sie in einer Abfrage oder sonstigem Code explizit auf das Feld (oder die Eigenschaft) verweisen müssen. Da solche Klassen andere Eigenschaften aufweisen können, werden wie im folgenden Beispiel gezeigt die Klassen, die Felder (und Metadateneigenschaften) repräsentieren, von der Angabe [ColumnAttribute] (die auf [Column] verkürzt werden kann) unterschieden.

[ContentType(Name="Customer")]
public partial class Customer : Item
{
    [Column]
    public Int32 CustomerId { get; set; }

    [Column]
    public String Name { get; set; }
}
VorsichtVorsicht

Falls das Listenschema geändert wird, nachdem die Entitätsklassen generiert wurden, können bei einer Lösung, die diese Entitätsklassen verwendet, Fehler auftreten.

Vererbung von Inhaltstypen

Die Inhaltstypklassen können die Vererbungsbeziehungen der eigentlichen Inhaltstypen, die sie repräsentieren, widerspiegeln. Der grundlegende Inhaltstyp in SharePoint Foundation wird als Item bezeichnet. Er stellt die Basisfelder bereit, die in allen Listenelementem vorhanden sind, wie z. B. Title. (Einige dieser Basisfelder, wie z. B. ID und Version, sind in SharePoint Foundation-Standardlistenansichten standardmäßig ausgeblendet). Es folgt eine vereinfachte Version der Klassendeklaration für den Inhaltstyp Item.

[ContentType(Name="Item")]
public partial class Item
{
    private Nullable<Int32> _id;
    private Nullable<Int32> _version;
    private String _title;

    [Column]
    public Nullable<Int32> Id { get; set; }

    [Column]
    public Nullable<Int32> Version { get; set; }

    [Column]
    public String Title { get; set; }

  // Other member declarations omitted for readability.
}

Alle anderen Inhaltstypklassen werden von Item abgeleitet. Deshalb müssen von diesen Klassen Eigenschaften, die sie erben, nicht explizit deklariert werden.

Beziehungen zwischen Listen und verzögertes Laden

Listen können permanente Beziehungen zueinander aufweisen, die in der objektrelationalen Infrastruktur durch die Klassen AssociationAttribute, EntityRef<TEntity> und EntitySet<TEntity> repräsentiert werden, die wie EntityList<TEntity> das Laden der repräsentierten Entität verzögern, bis zur Laufzeit zum ersten Mal auf die Entität zugegriffen wird. (Optional kann auch ein LookupList<T>-Objekt beim Repräsentieren der Beziehung beteiligt sein).

Wichtiger HinweisWichtig

In SharePoint Foundation ist für zwei Listen nur dann eine Zuordnung möglich, wenn eine der Listen eine Spalte enthält, die eine Nachschlagespalte für eine Spalte in der anderen Liste darstellt.

Entitätsverweise

Die EntityRef<TEntity>-Klasse repräsentiert das Listenelement auf der Singleton-Seite einer n:1- oder 1:1-Beziehung zwischen Listenelementen in unterschiedlichen Listen und ermöglicht das verzögerte Laden dieses Listenelements. Angenommen, jedem Teammitglied wird ein Sicherheitscode zugewiesen, der aus Sicherheitsgründen in einer separaten Liste getrennt von der Teammitgliederliste gespeichert wird. Eine TeamMember-Klasse repräsentiert den Elementtyp in der Liste Team Members, und die SecurityCode-Klasse repräsentiert den Elementtyp der Liste Security Codes. Da genau ein Sicherheitscode für jedes Teammitglied vorhanden ist, wird mit der TeamMember-Klasse ein privates Feld vom Typ EntityRef<TEntity> deklariert (wobei TEntity gleich SecurityCode ist), das ein Element in der Liste Security Codes repräsentiert. Die TeamMember-Klasse deklariert außerdem eine public-Eigenschaft vom Typ SecurityCode, die das private Feld umschließt. Diese Eigenschaft wird mit AssociationAttribute ergänzt. Wenn ein Objekt vom Typ TeamMember instanziiert wird, wird dessen SecurityCode-Wert nicht sofort geladen. Er wird nur dann geladen, wenn später im Code darauf verwiesen wird.

Es folgt ein Beispiel für die relevanten Bestandteile der TeamMember-Klassendeklaration. Beachten Sie bei diesem Beispiel Folgendes:

  • Die Beziehung zwischen den beiden Tabellen wird mit AssociationAttribute deklariert. (Die Verwendung der EntityRef<TEntity>-Klasse ermöglicht das verzögerte Laden, aber dadurch wird keine Listenbeziehung eingerichtet).

  • Die MultivalueType-Eigenschaft der AssociationAttribute-Klasse gibt an, ob für jedes Element nur ein entsprechendes Element in der zugehörigen Liste vorhanden ist, oder aber mehrere Elemente.

  • Die List-Eigenschaft der AssociationAttribute-Klasse gibt den Namen der zugehörigen Liste an, wobei die Groß-/Kleinschreibung beachtet wird.

[ContentType(Name = "TeamMember")]
public class TeamMember
{
    private EntityRef<SecurityCode> memberSecurityCode;

    [Association(List = "SecurityCodes", MultiValueType = AssociationType.Single)]
    public SecurityCode MemberSecurityCode
    {
        get { return memberSecurityCode.GetEntity(); }
        set { memberSecurityCode.SetEntity(value); }
    }

    // Declarations of other members suppressed for readability.
}

[ContentType(Name = "SecurityCode")]
public class SecurityCode
{
    [Column]
    public String Value { get; set; }

    // Declarations of other members suppressed for readability.
}
HinweisHinweis

Die Assessoren get- und set der MemberSecurityCode-Eigenschaft verwenden die Methoden GetEntity() und SetEntity(TEntity) anstelle des vertrauteren Assessorcodes ("return securityCode" und "securityCode=value"), da die Eigenschaft einen anderen Typ als das umschlossene Feld aufweist. Diese beiden Methoden erreichen das EntityRef<TEntity>-Objekt, um zu dem umschlossenen SecurityCode-Objekt zu gelangen.

Nun können Sie auf den Sicherheitscode eines Teammitglieds mit vertrauter objektorientierter punktierter Schreibweise zugreifen, und die Zuordnung kann in Abfragen als eine Art permanenter Verknüpfung der beiden Listen fungieren. Beispielsweise ist in der folgenden Abfrage member ein Element aus der Liste Team Members, aber MemberSecurityCode ist ein Verweis auf ein Element in der Liste Security Codes.

var result = from member in teamMembers
             where member.MemberSecurityCode.Value.Contains("guest")
             select member;

Die Beziehung kann entweder in der Klasse definiert werden, die den Nachschlagequelle-Inhaltstyp repräsentiert, oder in der Klasse, die den Zielinhaltstyp repräsentiert. In diesem Beispiel wird beschreiben, wie Sie die Beziehung zwischen den Listen Team Members und Security Codes mithilfe einer Eigenschaft der SecurityCode-Klasse definieren.

[ContentType(Name = "SecurityCode")]
public partial class SecurityCode
{
    [Column]
    public String Value { get; set; }

    private EntityRef<TeamMember> teamMember;

    [Association(List = "TeamMembers", MultiValueType = AssociationType.Single)]
    public TeamMember TeamMember
    {
        get { return this.teamMember.GetEntity(); }
        set { this.teamMember.SetEntity(value); }
    }
    // Other member declarations suppressed for readability.
}

Wenn Sie die Beziehung in der Zielklasse auf diese Weise deklarieren, werden Reverse-Lookups im aufrufenden Code ermöglicht. Sie können sogar die Beziehung in beiden Klassen deklarieren, um Suchen in beiden Richtungen zu ermöglichen. Weitere Informationen zu Reverse-Lookups finden Sie weiter unten unter Reverse-Lookups.

Entitätenmengen

Die EntitySet<TEntity>-Klasse repräsentiert die n-Seite einer n:1- oder n:n-Beziehung zwischen Listenelementen in unterschiedlichen Listen und ermöglicht das verzögerte Laden dieser Listenelemente. Angenommen, jedem Teammitglied werden mehrere Projekte zugewiesen und jedem Projekt werden mehrere Teammitglieder zugewiesen. Dies stellt eine n:n-Beziehung dar. Eine TeamMember-Klasse repräsentiert den Elementtyp in der Liste Team Members, und die Project-Klasse repräsentiert den Elementtyp der Liste Projects. Die TeamMember-Klasse deklariert ein privates Feld vom Typ EntitySet<TEntity> (wobei TEntity gleich Project ist), das eines oder mehrere Elemente in der Liste Projects repräsentiert. Das Feld ist mit einer public EntitySet<Project>-Eigenschaft mit der Bezeichnung AssignedProjects umschlossen. Wenn ein Objekt vom Typ TeamMember instanziiert wird, wird das von dieser AssignedProjects-Eigenschaft angegebene Objekt nicht sofort geladen. Er wird nur dann geladen, wenn später im Code darauf verwiesen wird.

[ContentType(Name = "TeamMember")]
public class TeamMember
{
    private EntitySet<Project> assignedProjects;

    [Association(List="Projects", MultiValueType=AssociationType.Multi)]
    public EntitySet<Project> AssignedProjects
    {
        get { return this.assignedProjects; }
        set { this.assignedProjects.Assign(value); }
    }

    // Declarations of other members suppressed for readability.
}

[ContentType(Name = "Project")]
public class Project
{
    [Column]
    public String Title { get; set; }

    // Declarations of other members suppressed for readability.
}
HinweisHinweis

Im vorherigen Codebeispiel und in anderen Codebeispielen, in der die Project-Klasse vorhanden ist, wird die Title-Eigenschaft deklariert, um die Title-Spalte zu repräsentieren. Dies ist eine vereinfachte Darstellung, um die Lesbarkeit zu erhöhen. Im tatsächlichen Code würde die Project-Klasse von einer Item-Klasse erben, die den Basisinhaltstyp von SharePoint Foundation darstellt. Da die Title-Eigenschaft in der Definition der Item-Klasse deklariert ist, würde sie hier in der Definition der Project-Klasse nicht deklariert werden.

Nachdem Sie diese Deklarationen erstellt haben, können Sie wie in diesem Beispiel veranschaulicht mithilfe einer Abfrage nach Teammitgliedern suchen, die einem bestimmten Projekt zugewiesen sind.

Project fiscalPlan = new Project() { Title="Fiscal year planning." };

TeamSite teamData = new TeamSite("http://DeptServer/TeamSite");

var filteredTeamMembers = from member in teamData.TeamMembers
                          where member.AssignedProjects.Contains(fiscalPlan) 
                          select member;

Wie bei dem Beispiel für EntityRef<TEntity> kann die Beziehung mit beiden Klassen, die die Inhaltstypen der beiden verwandten Listen repräsentieren, definiert werden. Im folgenden Beispiel wird gezeigt, wie dieselbe Beziehung zwischen Team Members und Projects in der Project-Klasse definiert würde.

[ContentType(Name = "Project")]
public class Project
{
    [Column]
    public String Title { get; set; }

    private EntitySet<TeamMember> assignedTeamMembers;

    [Association(List="Team Members", MultivalueType=AssociationType.Multi)]
    public EntitySet<TeamMember> AssignedTeamMembers
    {
        get { return this.assignedTeamMembers; }
        set { this.assignedTeamMembers.Assign(value); }
    }

    // Declarations of other members suppressed for readability.
}

Sie können die Beziehung in beiden Klassen deklarieren. Weitere Informationen finden Sie weiter unten unter Reverse-Lookups.

1:n- und n:1-Beziehungen

Im Abschnitt Entitätsverweise finden Sie ein Codebeispiel für eine 1:1-Beziehung zwischen Listen. Der Abschnitt Entitätenmengen enthält ein Codebeispiel für eine n:n-Beziehung. 1:n- und n:1-Beziehungen können ebenfalls dargestellt werden. Setzen wir das Beispiel aus dem Abschnitt Entitätenmengen fort, und stellen Sie sich vor, dass jedes Teammitglied nicht nur mehreren Projekten zugewiesen ist, sondern auch mehrere Projekte leitet. Für jedes Projekt gibt es aber nur einen einzigen Projektleiter. Zwischen den Listen Team Members und Projects besteht eine 1:n-Beziehung. Diese Beziehung kann wie im folgenden Codebeispiel veranschaulicht mit EntitySet<TEntity>-Membern in der TeamMember-Klasse und einem EntityRef<TEntity>-Member in der Projects-Klasse repräsentiert werden.

[ContentType(Name = "TeamMember")]
public class TeamMember
{
    private EntitySet<Project> projectsLedBy;

    [Association(List="Projects", MultivalueType=AssociationType.Multi)]
    public EntitySet<Project> ProjectsLedBy
    {
        get { return this.projectsLedBy; }
        set { this.projectsLedBy.Assign(value); }
    }

    // Declarations of other members suppressed for readability.
}

[ContentType(Name = "Project")]
public class Project
{
    [Column]
    public String Title { get; set; }

    private EntityRef<TeamMember> lead;

    [Association(List = "TeamMembers", MultiValueType = AssociationType.Single)]
    public TeamMember Lead
    {
        get { return this.lead.GetEntity(); }
        set { this.lead.SetEntity(value); }
    }

    // Declarations of other members suppressed for readability.
}

Nachschlagelisten mit mehreren zulässigen Werten

Die LookupList<T>-Klasse stellt die aktuellen Werte eines Nachschlagefelds dar, für das mehrere Werte zulässig sind. Es handelt sich also um eine Teilmenge der Werte aus der Zielspalte der Zielliste in einer Nachschlagelistenbeziehung. Eine Inhaltstypklasse kann ein privates Feld vom Typ LookupList<T> zum Speichern der nachgeschlagenen Werte der Spalte im aktuellen Listenelementobjekt aufweisen.

Ein solches Feld ist nicht erforderlich, da Sie eine EntitySet<TEntity>-Eigenschaft zum Repräsentieren einer Nachschlagespalte verwenden können. Ein solches Feld ist jedoch sinnvoll aufgrund der Unterschiede, wie LookupList<T> und EntitySet<TEntity> geladen werden. Angenommen, die beiden Inhaltstypklassen A und B sind identisch, außer dass A ein LookupList<T>-Feld zum Repräsentieren einer Nachschlagespalte und B ein EntitySet<TEntity>-Feld verwendet. In beiden Fällen wird beim Aufzählen der Abfrage (in der Regel in einer foreach-Schleife), insbesondere wenn die Schleife ein bestimmtes Listenelement des Inhaltstyps erreicht, eine Abfrage für die Datenbank ausgeführt, um die Eigenschaften des Objekts aufzufüllen, das das Listenelement repräsentiert. Für ein Objekt vom Typ A wird das LookupList<T>-Feld sofort mit den Werten der Nachschlagespalte aufgefüllt. Für nachfolgende Verweise auf dieses Feld (oder auf eine Eigenschaft, in die dieses Feld umschlossen ist) in der foreach-Schleife ist keine zweite Abfrage für die Datenbank erforderlich. Das Laden wird jedoch für Objekte vom Typ EntitySet<TEntity> verzögert. Wenn also eine foreach-Schleife ein bestimmtes Objekt vom Typ B erreicht, wird die EntitySet<TEntity>-Eigenschaft nicht sofort mit den Elementen aus der Zielliste aufgefüllt. Stattdessen wird das Laden verzögert, bis in der Schleife zum ersten Mal auf die Eigenschaft verwiesen wird. Dann ist eine zweite Abfrage für die Datenbank zum Auffüllen der Eigenschaft erforderlich.

Ein Beispiel für einen Aufruf der EntitySet<TEntity>-Eigenschaft finden Sie im folgenden Codebeispiel aus dem Abschnitt Entitätenmengen. In diesem Beispiel wird das Feld AssignedProjects der Liste Team Members als Nachschlagefeld für das Feld Title der Liste Projects konfiguriert, und dieses Feld wird so konfiguriert, dass mehrere Werte zulässig sind.

Project fiscalPlan = new Project() { Title="Fiscal year planning." };

TeamSite teamData = new TeamSite("http://DeptServer/TeamSite");

var filteredTeamMembers = from member in teamData.TeamMembers
                          where member.AssignedProjects.Contains(fiscalPlan) 
                          select member;

Da jedoch EntitySet<TEntity> das verzögerte Laden verwendet, muss der Wert der TeamMember.AssignedProjects-Eigenschaft bei jedem Verweis von der Inhaltsdatenbank abgefragt werden. Ein LookupList<T>-Objekt wird sofort geladen. Deshalb wird Code, mit dem eine IList<T>-Eigenschaft aufgerufen wird, die ein LookupList<T>-Feld umschließt, schneller ausgeführt als Code, mit dem die EntitySet<TEntity>-Eigenschaft aufgerufen wird.

Setzen wir das Beispiel fort, und stellen Sie sich vor, dass mit der TeamMember-Klasse ein privates Feld vom Typ LookupList<T> deklariert wird (wobei T gleich String ist), das die Werte des Felds AssignedProjects repräsentiert, die aus der Nachschlagezielspalte (Title) der Liste Projects stammen. Laut Konvention wird dieses private Feld mithilfe einer Verkettung (in Höckerschreibweise) des Nachschlagespaltennamens und einer pluralisierten Version der Zielspalte, aus der die Werte stammen, benannt. In diesem Fall wird das Feld assignedProjectsTitles benannt. Das Feld ist mit einer öffentlichen Eigenschaft vom Typ IList<String> umschlossen. Es wird laut Konvention mit einer Version des Feldnamens mit großen Anfangsbuchstaben benannt. In diesem Beispiel lautet die Eigenschaft AssignedProjectsTitles.

HinweisHinweis

Die Namen der IList<T>-Eigenschaft und des Felds LookupList<T> sind pluralisiert, da das Nachschlagefeld mehrere Werte zulässt. Die LookupList<T>-Klasse spielt keine Rolle beim Repräsentieren von Nachschlagefeldern, die nur einen einzigen Wert zulassen.

Für die TeamMember-Klasse ist nun die Beziehung mit der Projects-Liste deklariert, und es weist eine AssignedProjectsTitles-Eigenschaft auf, um auf die Werte des Listenelementfelds Assigned Projects zu verweisen. Nun müssen die Elemente irgendwie miteinander verknüpft werden. Zu diesem Zweck wird die AssignedProjectsTitles-Eigenschaft mit ColumnAttribute ergänzt. Vier Eigenschaften der des Attributs werden festgelegt:

  • Name="Assigned Projects" – gibt die Spalte an, deren Werte durch die AssignedProjectsTitles-Eigenschaft repräsentiert werden.

  • FieldType="Lookup" – gibt an, dass das durch die Name-Eigenschaft identifizierte Feld ein Nachschlagefeld in SharePoint Foundation ist.

  • IsLookupValue=true – gibt an, dass das Feld ein Nachschlagefeld ist.

  • LookupDisplayColumn="Title" – gibt die Zielspalte der Zielliste an.

Der folgende Code veranschaulicht die erforderlichen Deklarationen zur Unterstützung der Nachschlagebeziehung mit einer EntitySet<TEntity>-Eigenschaft und einer IList<T>-Eigenschaft, die ein LookupList<T>-Feld umschließt.

[ContentType(Name = "TeamMember")]
public class TeamMember
{
    [Column]
    public Int32 MemberID { get; set; }

    private EntitySet<Project> assignedProjects;

    private LookupList<String> assignedProjectsTitles;

    [Association(List="Projects", MultiValueType=Multi)]
    public EntitySet<Project> AssignedProjects
    {
        get { return this.assignedProjects; }
        set { this.assignedProjects.Assign(value); }
    }

    [Column Name="Assigned Projects", FieldType="Lookup", IsLookupValue="true", LookupDisplayColumn="Title"]
    public IList<String> AssignedProjectsTitles 
    {
        get { return this.assignedProjectsTitles; }
        set { this.assignedProjectsTitles.Assign(value); }
    }

    // Declarations of other members suppressed for readability.
}

[ContentType(Name = "Project")]
public class Project
{
    [Column]
    public String Title { get; set; }

    // Declarations of other members suppressed for readability.
}

Nachdem Sie diese Deklarationen erstellt haben, ist wie im folgenden Beispiel gezeigt eine Version der vorausgehenden Abfrage mit einer besseren Leistung möglich.

TeamSite teamData = new TeamSite("http://DeptServer/TeamSite");

var filteredTeamMembers = from member in teamData.TeamMembers
                          where member.AssignedProjectsTitles.Contains("Fiscal year planning.") 
                          select member;
HinweisHinweis

SPMetal generiert ein LookupList<T>-Element nur dann, wenn eine Nachschlagespalte auf eine Zielliste verweist, die selbst nicht durch von SPMetal generierten Code repräsentiert wird. Beispielsweise eine Zielliste, die ausgeblendet ist oder die von der Codegenerierung durch die Konfigurationsdatei von SPMetal ausgeschlossen wurde. Weitere Informationen finden Sie unter Außerkraftsetzen von SPMetal-Standardeinstellungen mit einer Parameter-XML-Datei.

Spezielle ID-LookupList-Objekte

Der Wert eines Felds vom Typ Lookup weist nach der Festlegung das Format id;#Anzeigewert auf. Dabei ist Anzeigewert der aus der Zielspalte der Zielliste abgerufene Wert, und id ist die ID des Listenelements in der Zielliste, deren Wert ausgewählt wurde. Dies wird in der Benutzeroberfläche von SharePoint Foundation unterdrückt. Wenn es eine Nachschlagebeziehung zwischen Listen gibt und die Nachschlagespalte mehrere Werte zulässt, muss die Lösung oft auf die IDs der Elemente in der Zielliste zugreifen, deren Zielspaltenwerte die Werte der Nachschlagespalte sind. Diese Werte können Sie über die EntitySet<TEntity>-Eigenschaft angeben, die die Elemente in der Zielliste repräsentiert. In diesem Fall wird jedoch Code ebenfalls schneller ausgeführt, wenn diese IDs in einem LookupList<T>-Feld gespeichert werden. Deshalb empfiehlt sich ein solches Feld und eine öffentliche IList<T>-Eigenschaft, um dieses Feld einzuschließen, wenn ein LookupList<T>-Feld zum Speichern der nachgeschlagenen Werte selbst vorhanden ist.

Die Benennungskonvention für diese speziellen ID-Felder und Eigenschaften ist identisch mit der Benennungskonvention für das Feld und die Eigenschaft, in denen die nachgeschlagenen Werte gespeichert sind, außer dass der zweite Teil des Namens "Ids" anstelle einer pluralisierten Version des Zielspaltennamens lautet. Das [Column]-Attribut der IList<T>-Eigenschaft ist ebenfalls identisch.

Das folgende Beispiel veranschaulicht die Deklarationen für Team Members und Projects, die um ein LookupList<T>-ID-Feld und die entsprechende Eigenschaft erweitert wurden.

[ContentType(Name = "TeamMember")]
public class TeamMember
{
    [Column]
    public Int32 MemberID { get; set; }

    private EntitySet<Project> assignedProjects;

    private LookupList<String> assignedProjectsTitles; 

    private LookupList<String> assignedProjectsIds; 


    [Association(List="Projects", MultiValueType=Multi)]
    public EntitySet<Project> AssignedProjects
    {
        get { return this.assignedProjects; }
        set { this.assignedProjects.Assign(value); }
    }

    [Column Name="Assigned Projects", FieldType="Lookup", IsLookupValue="true", LookupDisplayColumn="Title"]
    public IList<String> AssignedProjectsTitles 
    {
        get { return this.assignedProjectsTitles; }
        set { this.assignedProjectsTitles.Assign(value); }
    }

    [Column Name="Assigned Projects", FieldType="Lookup", IsLookupValue="true"]
    public IList<String> AssignedProjectsIds 
    {
        get { return this.assignedProjectsIds; }
        set { this.assignedProjectsIds.Assign(value); }
    }    

    // Declarations of other members suppressed for readability.
}

[ContentType(Name = "Project")]
public class Project
{
    [Column]
    public String Title { get; set; }

    // Declarations of other members suppressed for readability.
}

Nachschlagelisten, die nur einen Wert zulassen

Ein Nachschlagefeld mit einem einzigen Wert repräsentiert eine einfache 1:1-Beziehung. Das private Feld, in dem der Wert gespeichert wird, ist nur der Typ des Zielfelds, und nicht der LookupList<T>-Typ. Entsprechend weist die öffentliche Eigenschaft, die das Feld umschließt, denselben Typ auf, und nicht IList. (Genau genommen ist der Typ des Felds und der Eigenschaft die Entsprechung von Microsoft .NET Framework für den SharePoint Foundation-Typ des Zielfelds, was unter Typzuordnung: Von LINQ to SharePoint-Anbieter zu .NET näher erläutert wird). Darüber hinaus stehen die Namen des Felds und der Eigenschaft nicht im Plural, da nur ein einziger Wert vorhanden ist.

Betrachten wir einmal eine Variante des Beispiels im vorherigen Abschnitt. Angenommen, jedes Teammitglied wird nur einem einzigen Projekt zugewiesen, und die Zuweisung wird in der Project-Spalte aufgezeichnet, die ein Nachschlagefeld für die Title-Spalte der Liste Projects ist. Neben der Verwendung eines EntityRef<TEntity>-Felds zur Darstellung der Beziehung zwischen den beiden Listen würde mit der TeamMember-Klasse auch ein privates String-Feld mit dem Namen projectTitle und eine öffentliche String-Klasse, um diese zu umschließen, mit dem ProjectTitle deklariert. Beachten Sie, dass die Feld- und Eigenschaftsnamen zwar im Singular stehen, aber ansonsten die Benennungskonvention eines Nachschlagewerts mit Verkettung des Nachschlagefeldnamen und des Zielfeldnamens eingehalten wird.

[ContentType(Name = "TeamMember")]
public class TeamMember
{
    private EntityRef<Project> project;

    private String projectTitle;

    [Association(List="Projects", MultiValueType=AssociationType.Single)]
    public Project Project
    {
        get { return this.project.GetEntity(); }
        set { this.project.SetEntity(value); }
    }

    [Column Name="Project", FieldType="Lookup", IsLookupValue="true", LookupDisplayColumn="Title"]
    public String ProjectTitle 
    {
        get { return this.projectTitle.GetEntity(); }
        set { this.projectTitle.SetEntity(value); }
   }

    // Declarations of other members suppressed for readability.
}

[ContentType(Name = "Project")]
public class Project
{
    [Column]
    public String Title { get; set; }

    // Declarations of other members suppressed for readability.
}
HinweisHinweis

Für den speziellen Fall, dass mit dem Nachschlagefeld ein Wert in einem anderen Feld innerhalb derselben Tabelle nachgeschlagen wird, gibt es natürlich keine Beziehung zwischen Tabellen. Deshalb würden die Deklarationen des EntityRef<TEntity>-Felds und die umschließende Eigenschaft nicht vorhanden sein.

Reverse-Lookups

Sie können eine Listenbeziehung in beiden Inhaltstypklassen deklarieren. Dadurch können Sie Reverse-Lookups ausführen. Wenn es sich dabei jedoch um eine n:n-Beziehung handelt, weist die EntitySet<TEntity>-Eigenschaft in der Zielliste eine geringfügig unterschiedliche AssociationAttribute-Klasse auf. Die MultivalueType-Eigenschaft ist auf AssociationType.Backward, und nicht auf Multi festgelegt. Außerdem ist die Name-Eigenschaft auf den internen Namen der Quellnachschlagespalte festgelegt; d. h., das Ziel für ein Reverse-Lookup.

Siehe auch

Aufgaben

Vorgehensweise: Verwenden von SPMetal

Referenz

SPMetal

Konzepte

Nachverfolgen von Objektänderungen und vollständige Parallelität