Поделиться через


Классы сущности

Дата последнего изменения: 9 марта 2010 г.

Применимо к: SharePoint Foundation 2010

В этой статье
Использование SPMetal
Класс контекста данных
Списки, типы контента и элементы списков

В данном разделе представлены классы сущностей поставщика LINQ для SharePoint. Эти классы предоставляют облегченный объектно-ориентированный интерфейс между разрабатываемым объектно-ориентированным кодом C# и Microsoft Visual Basic и реляционной структурой таблиц базы данных контента Microsoft SharePoint Foundation. Они также предоставляют платформу для системы отслеживания изменений объектов.

Объектно-реляционный интерфейс позволяет создавать объектно-ориентированный код в запросах LINQ, а также обеспечивает создание, удаление и обновление элементов списка с помощью объектно-ориентированного кода, обращающегося к сущностям базы данных независимо от обычной объектной модели SharePoint Foundation. Также после установки объектно-реляционной платформы можно использовать ее для объектно-ориентированной бизнес-логики во всех ситуациях, где облегченная и отложенная загрузка, предоставляемая платформой, дает преимущества над обычной объектной моделью SharePoint Foundation. Использование классов сущностей вместо обычной объектной модели может также улучшить читаемость кода, когда ссылки на контент близки к коду запросов LINQ, то есть при использовании таких же классов сущностей, например как в цикле foreach, который выполняет итерацию по результатам запроса LINQ.

Использование SPMetal

Процесс написания классов, представленный в данной статье, может быть утомительным и приводить к ошибкам, поэтому SharePoint Foundation включает средство командной строки, называемое SPMetal, которое может использоваться для создания объявлений классов и свойств. Оно читает определения типов контента и схемы списков для списков целевого веб-сайта SharePoint Foundation, а затем создает из них классы сущностей, включая добавление атрибутов. Средство включено в SharePoint Foundation и обычно находится в папке %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\BIN. Рекомендуется использовать это средство.

Способ создания кода средством SPMetal можно настроить. Также благодаря тому, что все создаваемые им классы помечаются partial (Partial в Visual Basic), можно добавлять в них члены с помощью отдельного файла кода. Если с помощью SPMetal требуется заново создать классы сущностей, то перезаписывается только созданный им файл, а не файлы с пользовательскими членами. Дополнительные сведения об использовании SPMetal см. в разделе Практическое руководство. Использование программы SPMetal.

СоветСовет

Рекомендуется использовать SPMetal для создания всех объявлений классов, описанных в данной статье. Также обратите внимание, что примеры кода в данной статье не предназначены для копирования в разрабатываемый код, также они не являются точными копиями того, что может создать SPMetal. Каждый пример является упрощенным, исключающим избыточный для описываемого действия код. В частности, весь код, связанный с системой отслеживания изменений объектов, исключен из данных примеров. Дополнительные сведения об этой системе и поддерживающем ее коде см. в разделе Отслеживание изменений объектов и оптимистический параллелизм. Также добавление атрибутов для объявлений классов и свойств в данном разделе в целом проще, чем может потребоваться на практике, а некоторые атрибуты опущены полностью. Дополнительные сведения об атрибутах и их использовании см. в справочных разделах для следующих классов (и их членов):

Класс контекста данных

На вершине иерархии классов сущностей располагается класс DataContext, представляющий контент веб-сайта. Можно создать экземпляр объекта DataContext, передав его конструктору URL-адрес веб-сайта, чьи данные требуется смоделировать. Затем вызывается его метод GetList<T>(String), чтобы получить ссылку на определенный список. Ниже приведен пример:

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

Подразумевая, что был объявлен класс для представления типа контента "Announcement" (см. ниже), затем можно создавать запросы к teamAnns. Пример:

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

Обычно можно получить более читаемый и более объектно-ориентированный код вызовов, если объявлять класс, который наследуется от DataContext и имеет одно свойство для каждого из списков веб-сайта. Пример таких объявлений:

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

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

}
ПримечаниеПримечание

ListAttribute определяет свойство как представление определенного списка на веб-сайте, представленном объектом DataContext.

После создания этого объявления коду вызова не требуется явно вызывать метод GetList<T>(String), и такой код более нагляден.

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

var excitingAnns = from ann in teamData.Announcements
                   where ann.Title.EndsWith("!")
                   select ann;
Важное примечаниеВажно!

Конструктор класса TeamSite не загружает весь список "Announcements". Класс EntityList<TEntity> поддерживает отложенную загрузку. Объекты этого типа не загружаются до тех пор, пока на них не будет явно ссылаться последующий код.

Средство SPMetal всегда создает класс, происходящий от DataContext. Можно настроить, какие списки он будет моделировать. По умолчанию он объявляет свойство для каждого не скрытого списка веб-сайта.

ПримечаниеПримечание

Можно, конечно, добавить методы, события, поля и свойства, которые не представляют списки класса, производного от DataContext. Если добавляются такие члены, объявления должны быть в файле кода, отдельном от файла, созданного SPMetal. Это предотвращает перезаписывание кода, если требуется создать с помощью SPMetal код заново.

Списки, типы контента и элементы списков

Все решения, использующие LINQ для SharePoint, должны использовать классы сущностей для представления списков, типов контента и элементов списков SharePoint.

Класс EntityList<TEntity>представляет список SharePoint Foundation. Переменная типа TEntity является типом элементов списка; то есть типом контента. Свойству типа EntityList<TEntity> дано то же имя, что и представляемому им списку, например "Announcements" или "Calendar". TEntity обычно называется по единственному числу того же слова, например "Announcement", но если имя списка само по себе в единственном числе, может быть использовано другое слово. Например, в качестве типа для класса, представляющего список "Calendar" используется "CalendarEvent". Для пользовательского списка имя типа формируется посредством добавления к имени списка обозначения "Item". Например, пользовательский список "Books" будет иметь тип "BooksItem". Класс EntityList<TEntity> является запечатанным и не имеет открытого конструктора. Пример объявления свойства типа EntityList<TEntity> для определенного TEntity см. в разделе Класс контекста данных.

Типы содержимого, в свою очередь, должны объявляться как классы. К объявлению добавляется атрибут ContentTypeAttribute (который может быть сокращен до "ContentType"), как показано в следующем примере:

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

}

Каждое поле (и свойство метаданных) списка должно представляться открытым свойством в определении класса типа контента, если требуется явно сослаться на поле (или свойство) в запросе или другом коде. Так как такие классы могут иметь другие свойства, то классы, представляющие поля (и свойства метаданных) различаются по присвоению атрибута [ColumnAttribute], который может быть сокращен до [Column], как показано в примере:

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

    [Column]
    public String Name { get; set; }
}
Предупреждающая заметкаВнимание!

Если схема списка изменяется после создания классов сущностей, то решение, использующее эти классы сущностей, может завершиться ошибкой.

Наследование типов контента

Классы типов контента могут отражать отношения наследования действительных типов контента, которые они представляют. Фундаментальный тип контента в SharePoint Foundation называется "Item" (элемент). Он предоставляет базовые поля, которые имеются у всех элементов списка, такие как Заголовок. (Некоторые из этих базовых полей, такие как ID (идентификатор) и Version (версия), в стандартных представлениях списков SharePoint Foundation по умолчанию скрыты.) Упрощенный вариант объявления класса для типа контента "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.
}

Все другие классы типа контента происходят от класса "Item". Поэтому они не требуют явного объявления свойств, которые они наследуют.

Отношения между списками и отложенная загрузка

Списки могут иметь постоянные взаимоотношения между собой, представленные в объектно-реляционной инфраструктуре посредством атрибута AssociationAttribute и классов EntityRef<TEntity> и EntitySet<TEntity>, которые, подобно EntityList<TEntity>, откладывают загрузку представляемой ими сущности до тех пор, пока к сущности не будет осуществлен первый доступ во время выполнения. (Объект LookupList<T> также может быть задействован в представлении отношений.)

Важное примечаниеВажно!

В SharePoint Foundation два списка могут иметь связь, только если один список имеет столбец, являющийся столбцом просмотра для столбца другого.

Ссылки на сущности

Класс EntityRef<TEntity> представляет элемент списка и обеспечивает его отложенную загрузку на стороне "одного" в отношениях "многие к одному" и "один к одному" между элементами списков в разных списках. Например, представьте, что каждому члену группы назначен код безопасности, сохраненный с целью безопасности в отдельном от списка членов группы списке. Класс TeamMember представляет тип элементов в списке Team Members (Члены групп) и класс SecurityCode представляет тип элементов списка Security Codes (Коды безопасности). Так как существует точно один код безопасности для каждого члена группы, класс TeamMember объявляет поле private типа EntityRef<TEntity> (где TEntity — это SecurityCode), которое представляет элемент в списке Security Codes. Класс TeamMember также объявляет свойство public типа SecurityCode, которое размещает частное поле. Этому свойству добавляется атрибут AssociationAttribute. При создании экземпляра объекта типа TeamMember его значение SecurityCode загружается не сразу. Оно загружается только в том случае, если на него ссылается последующий код.

Далее приведен пример существенных частей объявления класса TeamMember. Обратите внимание на следующие аспекты данного примера:

  • Взаимоотношение между двумя таблицами объявлено с добавлением атрибута AssociationAttribute. (При использовании класса EntityRef<TEntity> предоставляется отложенная загрузка, но само по себе использование этого класса не устанавливает взаимоотношения в списках.)

  • Свойство MultivalueType атрибута AssociationAttribute задает наличие у каждого элемента только одного соответствующего элемента в связанном списке или нескольких.

  • Свойство List атрибута AssociationAttribute задает имя связанного списка с учетом регистра.

[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.
}
ПримечаниеПримечание

Методы доступа get и set свойства MemberSecurityCode используют методы GetEntity() и SetEntity(TEntity) вместо более привычного кода методов доступа ("return securityCode" и "securityCode=value"), так как свойство в действительности имеет тип, отличный от типа поля, для которого это свойство является оболочкой. Эти два метода обращаются к объекту EntityRef<TEntity>, чтобы получить доступ к объекту SecurityCode, который в нем размещается.

Теперь можно получить доступ к коду безопасности членов группы с помощью знакомой объектно-ориентированной точечной нотации, а связь может работать в запросах, подобных разновидности постоянной связи двух списков. В следующем запросе member — это элемент списка Team Members, а MemberSecurityCode — это ссылка на элемент в списке Security Codes.

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

Отношение может быть определено как в классе, представляющем тип контента источника просмотра, так и в классе, представляющем целевой тип контента. Далее приводится пример того, как определить отношение между списками Team Members и Security Codes с помощью свойства класса SecurityCode.

[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.
}

Подобное объявление отношения в целевом классе позволяет осуществлять обратный просмотр в вызывающем коде. Можно даже объявить отношение в обоих классах, чтобы позволить обратный просмотр в двух направлениях. Дополнительные сведения об обратном просмотре см. в разделе Обратный просмотр ниже.

Наборы сущностей

Класс EntitySet<TEntity> представляет сторону "многих" в отношениях "многие к одному" и "многие к многим" между элементами списков в различных списках и обеспечивает их отложенную загрузку. Например, предположим, что каждый член группы назначен на несколько проектов, а каждый проект имеет несколько членов группы, назначенных ему. Это отношение "многие к многим". Класс TeamMember представляет тип элементов в списке Team Members, а класс Project представляет тип элементов списка Projects. Класс TeamMember объявляет поле private типа EntitySet<TEntity> (где TEntity — это Project), представляющее один или несколько элементов в списке Projects. Поле размещается в свойстве public EntitySet<Project>, которое называется AssignedProjects. При создании экземпляра объекта типа TeamMember объект, на который ссылается свойство AssignedProjects, не загружается сразу же. Оно загружается только в том случае, если на него ссылается последующий код.

[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.
}
ПримечаниеПримечание

В предыдущем примере кода и других примерах, показывающих класс Project, существует объявление свойства Title, чтобы представить столбец Title (Заголовок). Это упрощение для читаемости. В действительном коде класс Project может происходить от класса Item, который представляет базовый тип контента SharePoint Foundation. Так как свойство Title объявлено в определении класса Item, оно может отсутствовать в определении класса Project.

Имея данные объявления, можно осуществлять запрос членов группы, назначенных на определенный проект, как в следующем примере:

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;

Как и в примере для EntityRef<TEntity>, оба класса, представляющих типы контента двух связанных списков, могут использоваться для определения отношений. В следующем примере показано, как то же отношение "Team Members к Projects" может быть объявлено в классе Project.

[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.
}

Можно объявить отношение в обоих классах. Дополнительные сведения см. в разделе Обратный просмотр ниже.

Отношения "один ко многим" и "многие к одному"

В разделе Ссылки на сущности дан пример кода для отношений между списками "один к одному". В разделе Наборы сущностей дан пример кода для отношений "многие к многим". Возможно представить также отношения "один ко многим" и "многие к одному". Продолжая пример раздела Наборы сущностей, представьте, что каждый член группы не только назначен на несколько проектов, но является руководителем нескольких проектов. Но каждый проект имеет только одного руководителя. Отношение руководителей в списке Team Members к списку Projects является "один ко многим". Это отношение может быть представлено с помощью членов EntitySet<TEntity> в классе TeamMember и члена EntityRef<TEntity> в классе Projects, как это показано в следующем коде.

[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.
}

Списки просмотра с допустимыми несколькими значениями

Класс LookupList<T> представляет текущие значения поля просмотра, позволяющего иметь несколько значений. Поэтому он является поднабором значений, находящихся в целевом столбце целевого списка в отношении просмотра для списка. Класс типа контента может иметь частное поле типа LookupList<T>, чтобы содержать просмотренные значения столбца в текущем объекте элемента списка.

Нет необходимости иметь такое поле, так как можно использовать свойство EntitySet<TEntity> для представления поля просмотра. Но может потребоваться иметь такое поле из-за различий в способе загрузки LookupList<T> и EntitySet<TEntity>. Представьте два класса типа контента, А и Б, которые одинаковы за исключением того, что для представления столбца просмотра класс А использует поле LookupList<T>, а класс Б использует EntitySet<TEntity>. В обоих случаях при перечислении запроса (обычно в цикле foreach), в частности при достижении циклом определенного элемента списка заданного типа контента, он выполняет запрос в базу данных, чтобы заполнить свойства объекта, представляющего элемент списка. Для объекта типа А поле LookupList<T> сразу же заполнено значениями столбца просмотра. Последующие ссылки на это поле (или на свойство, которое его размещает) в цикле foreach не требуют второго запроса к базе данных. Однако загрузка откладывается для объектов типа EntitySet<TEntity>, поэтому, когда цикл foreach достигает определенного объекта типа Б, свойство EntitySet<TEntity>не заполнено сразу элементами из целевого списка. Вместо этого загрузка отложена до первой ссылки на свойство в теле цикла. Когда такая ссылка возникает, требуется второй запрос в базу данных, чтобы заполнить свойство.

Пример вызова свойства EntitySet<TEntity> приведен в следующем коде из раздела Наборы сущностей. В этом примере поле AssignedProjects списка Team Members настроено в качестве поля просмотра для поля Title списка Projects и настроено для разрешения нескольких значений.

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;

Но так как EntitySet<TEntity> использует отложенную загрузку, значение свойства TeamMember.AssignedProjects должно запрашиваться из базы данных контента каждый раз при ссылке на него. Объект LookupList<T> загружается сразу же, поэтому код, вызывающий свойство IList<T> (размещающее поле LookupList<T>) выполняется быстрее, чем код, вызывающий свойство EntitySet<TEntity>.

Чтобы продолжить пример, представьте, что класс TeamMember объявляет поле private типа LookupList<T> (где T — это String), представляющее значения поля AssignedProjects, приходящие из целевого столбца просмотра (Title) списка Projects. По соглашению об именовании имя этого частного поля формируется добавлением имени столбца просмотра (в "верблюжьем" стиле) и имени целевого столбца, от которого происходят значения, в множественном числе. В данном случае поле называется assignedProjectsTitles. Поле размещается в открытом свойстве типа IList<String>. В соответствии с соглашением об именовании оно называется именем поля, написанным с заглавных букв, поэтому в данном случае свойство называется AssignedProjectsTitles.

ПримечаниеПримечание

Имена свойства IList<T> и поля LookupList<T> имеют множественное число, так как поле просмотра позволяет иметь несколько значений. Класс LookupList<T> не играет роли в представлении полей просмотра, которые позволяют задавать только единственное значение.

Для класса TeamMember теперь объявлено отношение со списком Projects, и у этого класса теперь есть свойство AssignedProjectsTitles, чтобы ссылаться на значения поля элемента списка Assigned Projects. Теперь требуется связать их вместе. Это достигается добавлением свойству AssignedProjectsTitles атрибута ColumnAttribute. Имеется четыре свойства атрибута:

  • Name="Assigned Projects". Задает столбец, значения которого представлены свойством AssignedProjectsTitles.

  • FieldType="Lookup". Указывает, что поле, определяемое свойством Name, имеет тип "Lookup" в SharePoint Foundation.

  • IsLookupValue=true. Указывает, что поле является полем просмотра.

  • LookupDisplayColumn="Title". Задает целевой столбец целевого списка.

В следующем коде показаны определения, необходимые для поддержки отношений просмотра для свойств EntitySet<TEntity> и IList<T> (размещающих поле LookupList<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.
}

С помощью этих объявлений возможно создать версию предыдущего запроса, имеющую большую производительность, как показано в следующем примере.

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

var filteredTeamMembers = from member in teamData.TeamMembers
                          where member.AssignedProjectsTitles.Contains("Fiscal year planning.") 
                          select member;
ПримечаниеПримечание

SPMetal создает LookupList<T> только в том случае, когда столбец просмотра указывает на целевой список, не представленный в коде, создаваемом SPMetal (например, скрытый целевой список или исключенный из процесса создания кода с помощью файла конфигурации SPMetal). Дополнительные сведения см. в разделе Переопределение значений SPMetal по умолчанию с помощью XML-файла параметров.

Объекты особых идентификаторов LookupList

Значение поля типа Lookup после того, как оно задано, представлено в форме идентификатор;#отображаемое_имя, где отображаемое_имя — это значение, запрашиваемое из целевого столбца целевого списка, а идентификатор — это идентификатор элемента списка в целевом списке, чье значение было выбрано. Это поле скрыто в пользовательском интерфейсе SharePoint Foundation. Если существует отношение просмотра между списками и есть возможность размещать в столбце просмотра несколько значений, решению часто требуется доступ к идентификаторам элементов в целевом списке, чьи целевые значения столбцов являются значениями столбца просмотра. Можно ссылаться на эти значения с помощью свойства EntitySet<TEntity>, представляющего элементы в целевом списке. Но в этом случае код также выполняется быстрее, если эти идентификаторы сохраняются в поле LookupList<T>. По этой причине рекомендуется иметь это поле и открытое свойство IList<T> (размещающее поле), если имеется поле LookupList<T>, чтобы размещать сами просмотренные значения.

Соглашение об именовании этих особых полей идентификаторов и свойств такое же, как для полей и свойств, содержащих значения подстановки, за исключением того, что вторая часть каждого имени имеет метку "Ids" вместо имени целевого столбца во множественном числе. Атрибут [Column] в свойстве IList<T> такой же.

В следующем примере показаны объявления для "Team Members" и "Projects", расширенные для добавления поля идентификатора LookupList<T> и его соответствующего свойства.

[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.
}

Списки просмотра, позволяющие размещать только одно значение

Поле просмотра с одним значением представляет простое отношение "один к одному". Поэтому частное поле, сохраняющее значение, имеет только тип целевого поля, а не тип LookupList<T>. Сходным образом открытое свойство, размещающее это поле, имеет такой же тип, а не IList. (Строго говоря, тип поля и свойства является эквивалентом Microsoft .NET Framework типа целевого поля SharePoint Foundation, как это описано в разделе Сопоставление типов: от поставщика LINQ to SharePoint к .NET.) Также благодаря тому, что в данном случае имеется только одно значение, имена полей и свойств имеют единственное число.

Рассмотрим вариант примера из предыдущего раздела. Представьте, что каждый член группы назначен только на один проект и назначение записано в столбце Project, который является полем просмотра для столбца Title списка Projects. В дополнение к использованию поля EntityRef<TEntity> для представления отношений между двумя списками класс TeamMember может также объявить частное поле String с именем projectTitle и открытый класс String (чтобы размещать его) с именем ProjectTitle. Обратите внимание, что хотя имена поля и свойства имеют единственное число, они следуют соглашению об именовании, формируя имя значения просмотра посредством добавления имени поля просмотра и имени целевого поля.

[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.
}
ПримечаниеПримечание

В особом случае, где поле просмотра просматривает значение в другом поле той же таблицы, нет отношений между таблицами; по этой причине объявления поля EntityRef<TEntity> и свойства, которое его размещает, могут отсутствовать.

Обратный просмотр

Можно объявить отношения списка в обоих классах типа контента. Это позволит производить обратный просмотр. Однако при выполнении этого в случае отношения "один ко многим" свойство EntitySet<TEntity> в целевом списке имеет немного другой атрибут AssociationAttribute. В частности, свойству MultivalueType задано значение AssociationType.Backward вместо Multi. Также свойство Name задано для внутреннего имени исходного столбца просмотра; то есть для целевого столбца обратного просмотра.

См. также

Задачи

Практическое руководство. Использование программы SPMetal

Ссылка

SPMetal

Концепции

Отслеживание изменений объектов и оптимистический параллелизм