Entity 类

上次修改时间: 2010年3月9日

适用范围: SharePoint Foundation 2010

本文内容
使用 SPMetal
数据上下文类
列表、内容类型和列表项

本主题介绍 LINQ to SharePoint 提供程序的实体类。这些类在面向对象的 C# 或 Microsoft Visual Basic 代码与 Microsoft SharePoint Foundation 内容数据库的关系表结构之间提供一种轻型的对象关系接口。这些类还提供对象更改跟踪系统的基础。

通过面向对象的接口,可以在 LINQ 查询中使用面向对象的代码,也可以使用引用数据库实体的面向对象的代码来创建、删除和更改列表项,而与常规 SharePoint Foundation 对象模型无关。此外,在对象关系框架就绪之后,只要该框架提供的轻型和延迟加载相对于常规 SharePoint Foundation 对象模型更具优势,就可以在这些情况下将框架用于面向对象的业务逻辑。当内容引用与使用实体类的 LINQ 查询(如在循环访问 LINQ 查询的结果的 foreach 循环中)极为接近时,使用相同的实体类而不去使用常规对象模型还可能会提高代码的可读性。

使用 SPMetal

编写本文中讨论的类是一件相当繁琐的事情且容易出错,因此 SharePoint Foundation 包括了一种称为 SPMetal 的命令行工具,使用此工具可以生成类和属性声明。此工具将读取目标 SharePoint Foundation 网站中的内容类型定义和列表的列表架构,然后根据这些内容生成包括属性效果在内的实体类。此工具随 SharePoint Foundation 一起提供,通常可在文件夹 %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\BIN 中找到它。推荐您使用此工具。

您可以自定义 SPMetal 生成代码的方式。此外,由于此工具生成的所有类都标记为 partial(在 Visual Basic 中为 Partial),因此您可以在单独的代码文件中向这些类添加成员。如果需要使用 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 to SharePoint 的任何解决方案都必须使用实体类表示 SharePoint 列表、内容类型和列表项。

EntityList<TEntity> 类表示 SharePoint Foundation 列表。类型变量 TEntity 表示列表项的类型(即内容类型)。为 EntityList<TEntity> 类型的属性指定的名称与它所表示的列表的名称相同,如"Announcements"或"Calendar"。TEntity 通常用同一个词语的单数形式命名(如"Announcement"),但如果列表名称本身为单数,则可能需要使用一个不同的词语。例如,将"CalendarEvent"用作表示 Calendar 列表的类的类型。对于自定义列表,类型名称是通过连接列表名称与"Item"一词构成的。例如,将为自定义"Books"列表指定类型"BooksItem"。EntityList<TEntity> 类是密封的,没有公共构造函数。有关将属性类型声明为特定 TEntity 的 EntityList<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 中的基本内容类型称为"项"。该内容类型提供所有列表项都具有的基本字段,如"标题"。(在标准的 SharePoint Foundation 列表视图中默认情况下会隐藏其中一些基本字段,如"ID"和"版本"。)"项"内容类型的类声明的简化版本如下所示。

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

其他所有内容类型类都派生自项。因此,这些类无需明确声明其继承的任何属性。

列表和延迟加载之间的关系

列表相互之间可以具有永久关系,并且这些关系在对象关系结构中由 AssociationAttribute 以及 EntityRef<TEntity>EntitySet<TEntity> 类进行表示;与 EntityList<TEntity> 类似,这样会延迟加载它们所表示的实体,直至在运行时首次实际访问实体。(表示关系也会用到 LookupList<T> 对象,这是可选的。)

重要注释重要信息

在 SharePoint Foundation 中,只有在一个列表中包含用于查找另一个列表中某一列的列时,这两个列表才会具有关联。

实体引用

EntityRef<TEntity> 类表示并提供不同列表中列表项之间的多对一或一对一关系的一端列表项的延迟加载。例如,假设为每位小组成员都分配一个安全代码,而且出于安全原因,该代码存储在不同于小组成员列表的单独列表中。TeamMember 类表示"小组成员"列表上的项的类型,而 SecurityCode 类表示"安全代码"列表上的项的类型。由于每个小组成员正好对应一个安全代码,因此 TeamMember 类会声明一个 EntityRef<TEntity> 类型的 private 字段(其中 TEntity 是 SecurityCode),用来表示"安全代码"列表中的项。TeamMember 类还会声明一个 SecurityCode 类型的 public 属性,用来包装私有字段。此属性就是用 AssociationAttribute 加以修饰的属性。当实例化 TeamMember 类型的对象时,不会立即加载此对象的 SecurityCode 值。当且仅当在后面的代码中引用到它时才会进行加载。

下面是 TeamMember 类声明的相关部分的示例。关于此示例请注意以下方面:

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

备注

MemberSecurityCode 属性的 get 和 set 访问器使用 GetEntity()SetEntity(TEntity) 方法,而不使用更加熟悉的访问器代码("return securityCode"和"securityCode=value"),原因是该属性的类型实际上与其包装的字段的类型不相同。这两个方法都可以访问 EntityRef<TEntity> 对象以处理其包装的 SecurityCode 对象。

现在,可以使用熟悉的面向对象的点表示法访问小组成员的安全代码,并且关联在查询中可以像两个列表的一种永久联结那样起作用。例如,在下面的查询中,member 是来自"小组成员"列表中的一项,而 MemberSecurityCode 则是对"安全代码"列表中的某一项的引用。

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

既可以在表示查找源内容类型的类中定义关系,也可以在表示目标内容类型的类中定义关系。下面演示如何使用 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 类表示"小组成员"列表上的项的类型,而 Project 类表示"项目"列表上的项的类型。TeamMember 类会声明一个 EntitySet<TEntity> 类型的 private 字段(其中 TEntity 是 Project),用来表示"项目"列表中的一个或多个项。该字段用称为 AssignedProjects 的 public EntitySet<Project> 属性进行包装。当实例化 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 属性来表示"标题"列。这是为了可读性而进行的一种简化。在实际的代码中,Project 类将从表示 SharePoint Foundation 的基内容类型的 Item 类继承。因为 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> 示例的情况一样,表示两个相关列表的内容类型的两个类中的任一个都可以用于定义关系。下面演示如何在 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.
}

在这两个类中都可以声明关系。有关详细信息,请参阅下面的反向查找。

一对多和多对一关系

在实体引用部分中,具有一个在列表之间编码一对一关系的示例。在实体集部分中,具有一个编码多对多关系的示例。表示一对多和多对一关系也是可能的。若要继续实体集部分的示例,假设每位小组成员不仅分配有多个项目,同时还是多个项目的领导者。但每个项目只有一个领导者。从"小组成员"列表到"项目"列表的领导关系就是一对多关系。可使用 TeamMember 类中的 EntitySet<TEntity> 成员和 Projects 类中的 EntityRef<TEntity> 成员表示此关系,如以下代码中所示。

[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> 加载方式不同的缘故,您可能希望具有该字段。请考虑 A 和 B 这两个内容类型类,它们唯一的不同点是:A 使用 LookupList<T> 字段来表示查找列,而 B 则使用 EntitySet<TEntity>。在这两种情况下,当枚举查询时(通常采用 foreach 循环),特别是当此循环到达内容类型的特定列表项时,会查询数据库以填充表示该列表项的对象的属性。对于 A 类型的对象,会立即用查找列的值填充 LookupList<T> 字段。以后在 foreach 循环中引用该字段(或包装该字段的属性)时,不需要第二次查询数据库。但是,将延迟加载 EntitySet<TEntity> 类型的对象,因此当 foreach 循环到达 B 类型的特定对象时,不会立即用目标列表中的项填充 EntitySet<TEntity> 属性。相反,将继续延迟加载,一直到首次在循环体中引用该属性。进行此次引用时,需要第二次查询数据库以填充该属性。

调用 EntitySet<TEntity> 属性的示例位于实体集部分的以下代码中。在该示例中,"小组成员"列表的"AssignedProjects"字段配置为针对"项目"列表的"Title"字段的查阅字段,此字段还配置为允许多个值。

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> 对象会立即加载,因此与调用 EntitySet<TEntity> 属性的代码相比,调用包装 LookupList<T> 字段的 IList<T> 属性的代码的执行性能要更佳。

若要继续该示例,假设 TeamMember 类声明了一个表示"AssignedProjects"字段值(来自于"项目"列表的查找目标列"Title")的类型为 LookupList<T> 的 private 字段(其中 T 是 String)。按照惯例,该私有字段按照查找列名称和复数形式的目标列(从中提取值)串联起来的方式(Camel 大小写格式)进行命名。在此情形下,该字段命名为 assignedProjectsTitles。该字段用 IList<String> 类型的公共属性包装起来。它按照惯例用 title 大小写格式的字段名进行命名,因此在此情形下,该属性名为 AssignedProjectsTitles

备注

IList<T> 属性和 LookupList<T> 字段的名称为复数,因为查阅字段允许多个值。LookupList<T> 类在表示仅允许单个值的查阅字段上不起任何作用。

TeamMember 类此时已与声明的"项目"列表建立了关系并且具有 AssignedProjectsTitles 属性 (Property) 用来引用"已分配项目"列表项字段的值。现在需要做的是将这两个列表挂接起来。通过用 ColumnAttribute 修饰 AssignedProjectsTitles 属性 (Property) 可以实现这一点。该属性 (Attribute) 的四个属性 (Property) 设置为:

  • Name="Assigned Projects" - 指定其值由 AssignedProjectsTitles 属性表示的列。

  • FieldType="Lookup" - 指定由 Name 属性标识的字段是 SharePoint Foundation 中的类型 Lookup。

  • 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 生成的代码进行表示(例如,SPMetal 的配置文件已在代码生成中隐藏或排除的目标列表)时,SPMetal 才会生成一个 LookupList<T>。有关详细信息,请参阅用参数 XML 文件替代 SPMetal 默认值

特殊的"ID"LookupList 对象

"Lookup"类型字段的值经设置后的形式为 id;#display_value,其中 display_value 是从目标列表的目标列派生的值,id 是选定其值的目标列表中的列表项的 ID。SharePoint Foundation UI 中禁止显示此字段。通常,当列表和查找列之间存在一个查找关系允许多个值时,您的解决方案将需要访问目标列表(其目标列的值为查找列的值)中的项的 ID。可以通过表示目标列表中项的 EntitySet<TEntity> 属性来引用这些值。但是,同样是在此情况下,如果这些 ID 存储在 LookupList<T> 字段中,则代码会运行的更快。出于此原因,最好是使用这样一个字段,并用一个公共 IList<T> 属性来包装该字段,这样无论何时,您都可以使用 LookupList<T> 字段来保存查找值自身。

除了每个名称的第二部分是"Ids"而不是复数形式的目标列名称以外,这些特殊"ID"字段和属性的命名约定和保存查找值的字段和属性的命名约定基本相同。IList<T> 属性上的 [Column] 属性也是这种情况。

下面的示例演示扩展"小组成员"和"项目"声明以添加"ID"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。(严格说来,字段和属性的类型是目标字段的 SharePoint Foundation 类型的 Microsoft .NET Framework 等效项,如 类型映射:从 LINQ to SharePoint 提供程序映射到 .NET中所述。)而且,因为只有一个值,字段和属性的名称不采用复数形式。

请考虑前面部分的示例上的变体。假设每个小组成员只分配到一个项目并且此种分配记录在"项目"列中,该列是针对"项目"列表的"标题"列的查阅字段。除了使用 EntityRef<TEntity> 字段来表示这两个列表之间的关系以外,TeamMember 类还将声明一个名为 projectTitle 的私有 String 字段,并声明一个公共 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

概念

对象更改跟踪和乐观并发