Clases de entidad

Última modificación: martes, 09 de marzo de 2010

Hace referencia a: SharePoint Foundation 2010

En este artículo
Uso de SPMetal
Clase de contexto de datos
Listas, tipos de contenido y elementos de lista

En este tema se describen las clases de entidad del proveedor LINQ to SharePoint. Estas clases proporcionan una interfaz relacional de objetos ligera entre el código C# o Microsoft Visual Basic orientado a objetos y la estructura de tabla relacional de las bases de datos de contenido de Microsoft SharePoint Foundation. También proporcionan la base del sistema de seguimiento de cambios en el objeto.

La interfaz relacional de objetos habilita código orientado a objetos en consultas LINQ y, además, habilita la creación, eliminación y actualización de elementos de lista usando código orientado a objetos que hace referencia a entidades de bases de datos independientemente del modelo de objetos de SharePoint Foundation regular. Asimismo, una vez que se ha implementado el marco de objetos relacionales, puede usarse para lógica de negocios orientada a objetos en cualquier situación en que la carga ligera y diferida que provee el marco ofrezca ventajas respecto del modelo de objetos de SharePoint Foundation regular. Usando las clases de entidad en lugar del modelo de objetos regular también puede mejorar la legibilidad del código cuando la referencia de contenido se encuentra muy próxima al código de consulta LINQ que usa las mismas clases de entidad, como en un bucle foreach que se itera en el resultado de una consulta LINQ.

Uso de SPMetal

La escritura de las clases que se describen en este artículo puede ser bastante tediosa y propensa a errores. Por esta razón, SharePoint Foundation incluye una herramienta de línea de comandos, llamada SPMetal, que se puede usar para generar las declaraciones de clases y propiedades. Esta herramienta lee las definiciones de tipos de contenido y los esquemas de las listas de un sitio web de SharePoint Foundation de destino y, a continuación, genera clases de entidad, incluidas las decoraciones de atributos, a partir de ellas. La herramienta está incluida en SharePoint Foundation y, por lo general, se encuentra en la carpeta %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\BIN. Recomendamos el uso de esta herramienta.

Puede personalizar la forma en que SPMetal genera el código. Es más, dado que todas las clases que genera están marcadas como partial (Partial en Visual Basic), es posible agregar miembros en un archivo de código independiente. Si necesita volver a generar las clases de entidad con SPMetal, solo se sobrescribe el archivo generado, no el archivo con los miembros personalizados. Para obtener información sobre cómo usar SPMetal, vea Procedimiento para usar SPMetal.

SugerenciaSugerencia

Recomendamos usar SPMetal para generar todas las declaraciones de las clases que se describen en este artículo. Además, los ejemplos de código que se incluyen en este artículo no están diseñados para que el usuario los copie en su propio código ni son copias exactas de los que produciría SPMetal. Se han simplificado para suprimir el código que no está relacionado con el tema en cuestión. En particular, se suprime todo el código relacionado con el sistema de seguimiento de cambios en el objeto en estos ejemplos. Para obtener más información sobre el sistema y el código en que está basado, vea Seguimiento de cambios en objetos y concurrencia optimista. Asimismo, las decoraciones de atributos en las declaraciones de clases y propiedades de este tema son normalmente más simples que las que se necesitarían en la práctica, y algunos atributos se omiten directamente. Para obtener más información sobre los atributos y sus usos, vea los temas de referencia de las siguientes clases (y sus miembros):

Clase de contexto de datos

En la parte superior de la jerarquía de clases de entidad se encuentra la clase DataContext, que representa el contenido de un sitio web. Puede crear una instancia de un objeto DataContext pasando a su constructor la dirección URL del sitio web cuyos datos desea modelar. A continuación, llame al método GetList<T>(String) para obtener una referencia a una lista específica. A continuación, se incluye un ejemplo:

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

Si ha declarado una clase para que represente el tipo de contenido "Announcement" (vea a continuación), podría consultar teamAnns. Por ejemplo:

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

Por lo general, se puede obtener código de llamada más legible, y más orientado a objetos, si se declara una clase que hereda de DataContext y que tiene una propiedad para cada una de las listas del sitio web. A continuación se incluye un ejemplo de este tipo de declaración.

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

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

}

Nota

ListAttribute identifica la propiedad como si representara una lista específica del sitio web representado por el objeto DataContext.

Una vez implementada esta declaración, el código de llamada no necesita la llamada explícita del método GetList<T>(String) y es más autodocumentado.

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

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

El constructor de la clase TeamSite no carga toda la lista Announcements. La clase EntityList<TEntity> admite la carga diferida. Los objetos de este tipo no se cargan hasta que se hace referencia explícita a ellos en código posterior.

La herramienta SPMetal siempre genera una clase que deriva de DataContext. Puede configurar qué listas modelará. De forma predeterminada, declara una propiedad para cada lista no oculta del sitio web.

Nota

Desde luego, es posible agregar métodos, eventos, campos y propiedades que no representan listas a una clase derivada de DataContext. Si se agregan estos miembros, las declaraciones deberían estar en un archivo de código independiente del archivo generado por SPMetal. Esto impide que el código se sobrescriba en el caso de que sea necesario regenerar el código que produce SPMetal.

Listas, tipos de contenido y elementos de lista

Toda solución que use LINQ to SharePoint debe usar clases de entidad para representar las listas, tipos de contenido y elementos de lista de SharePoint.

La clase EntityList<TEntity> representa una lista de SharePoint Foundation. La variable de tipo, TEntity, es el tipo de los elementos de lista, es decir, el tipo de contenido. Una propiedad del tipo EntityList<TEntity> recibe el mismo nombre que la lista que representa, como "Announcements" o "Calendar". Por lo general, TEntity recibe el nombre en singular del mismo término, como "Announcement", pero si el nombre de la lista ya está en singular, puede usarse otro término. Por ejemplo, "CalendarEvent" se usa como tipo para la clase que representa una lista Calendar. Para una lista personalizada, el nombre del tipo se construye concatenando el nombre de la lista con el término "Item". Por ejemplo, una lista "Books" personalizada recibe el tipo "BooksItem". La clase EntityList<TEntity> está sellada y no tiene constructor público. Vea la sección Clase de contexto de datos para obtener un ejemplo de declaración de propiedades del tipo EntityList<TEntity> para una TEntity específica.

Los tipos de contenido deben declararse como clases. La declaración se decora con un atributo ContentTypeAttribute (que puede abreviarse a "ContentType"), como se muestra en este ejemplo.

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

}

Cada campo (y propiedad de metadatos) de una lista debe representarse con una propiedad pública en la definición de clase de tipo de contenido si se debe hacer referencia explícita al campo (o propiedad) en una consulta u otro código. Debido a que dichas clases pueden tener otras propiedades, las que representan campos (y propiedades de metadatos) se distinguen por la decoración [ColumnAttribute] (que puede abreviarse a [Column]), como se muestra en este ejemplo.

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

    [Column]
    public String Name { get; set; }
}
Nota de precauciónPrecaución

Si se modifica el esquema de la lista después de generar las clases de entidad, la solución que use dichas clases de entidades puede producir un error.

Herencia de tipos de contenido

Las clases de tipo de contenido pueden reflejar las relaciones de herencia de los tipos de contenido que representan. El tipo de contenido fundamental de SharePoint Foundation se llama Item. Proporciona los campos básicos que tienen todos los elementos de lista, como Título. (Algunos de estos campos básicos, como ID y Versión, están ocultos de forma predeterminada en vistas de lista de SharePoint Foundation estándar.) Una versión simplificada de la declaración de clases del tipo de contenido Item es la siguiente.

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

Todas las demás clases de tipo de contenido derivan de Item. En consecuencia, no es necesario que declaren explícitamente ninguna de las propiedades que heredan.

Relaciones entre listas y carga diferida

Las listas pueden tener relaciones permanentes entre sí que se representan en la infraestructura relacional de objetos mediante la clase AssociationAttribute y las clases EntityRef<TEntity> y EntitySet<TEntity> que, al igual que EntityList<TEntity>, difieren la carga de la entidad que representan hasta la primera vez que se tiene acceso a la entidad en tiempo de ejecución. (Como opción, un objeto LookupList<T> también participa en la representación de la relación.)

Nota importanteImportante

En SharePoint Foundation, dos listas pueden tener una asociación solo si una de ellas tiene una columna que es una búsqueda de una columna de la otra.

Referencias a entidades

La clase EntityRef<TEntity> representa el elemento de lista del lado del singleton de una relación de varios a uno o uno a uno entre los elementos de diferentes listas, y proporciona su carga diferida. Por ejemplo, suponga que a cada miembro de un equipo se le asigna un código de seguridad que se almacena en una lista independiente de la lista de miembros del equipo por razones de seguridad. Una clase TeamMember representa el tipo de elementos de la lista Miembros del equipo y una clase SecurityCode representa el tipo de elementos de la lista Códigos de seguridad. Debido a que hay exactamente un código de seguridad por cada miembro del equipo, la clase TeamMember declara un campo private del tipo EntityRef<TEntity> (donde TEntity es SecurityCode) que representa un elemento de la lista Códigos de seguridad. La clase TeamMember también declara una propiedad public, del tipo SecurityCode, que encapsula el campo privado. Ésta es la propiedad que se decora con el atributo AssociationAttribute. Cuando se crea una instancia de un objeto del tipo TeamMember, su valor SecurityCode no se carga inmediatamente. Se carga si se hace referencia a él en código posterior, y solo en ese caso.

El siguiente es un ejemplo de las partes relevantes de la declaración de clases TeamMember. Tenga en cuenta lo siguiente acerca de este ejemplo:

  • La relación entre las dos tablas se declara con la decoración AssociationAttribute. (El uso de la clase EntityRef<TEntity> permite la carga diferida, pero no establece en sí una relación entre listas.)

  • La propiedad MultivalueType de AssociationAttribute especifica si cada elemento tiene solo un elemento correspondiente en la lista asociada o varios.

  • La propiedad List de AssociationAttribute especifica el nombre de la lista asociada que distingue entre mayúsculas y minúsculas.

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

Nota

Los evaluadores get y set de la propiedad MemberSecurityCode usan los métodos GetEntity() y SetEntity(TEntity) en lugar del código de evaluador más familiar ("return securityCode" y "securityCode=value"), ya que la propiedad es de un tipo distinto del campo que encapsula. Ambos métodos alcanzan el objeto EntityRef<TEntity> para tocar el objeto SecurityCode que encapsula.

Ahora es posible tener acceso al código de seguridad de un miembro del equipo con el sistema conocido de notación de puntos orientada a objetos y la asociación puede funcionar en consultas como un tipo de combinación permanente de las dos listas. Por ejemplo, en la siguiente consulta, member es un elemento de la lista Miembros del equipo, pero MemberSecurityCode es una referencia a un elemento de la lista Códigos de seguridad.

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

La relación puede definirse en la clase que representa el tipo de contenido de origen de búsqueda o en la clase que representa el tipo de contenido de destino. A continuación se describe el procedimiento para definir la relación entre las listas Miembros del equipo y Códigos de seguridad usando una propiedad de la clase 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.
}

La declaración de la relación en la clase de destino de esta forma permite invertir las búsquedas en el código de llamada. Puede incluso declarar la relación en ambas clases, para habilitar búsquedas en ambas direcciones. Para obtener más información acerca de búsquedas inversas, vea Búsquedas inversas a continuación.

Conjuntos de entidades

La clase EntitySet<TEntity> representa el lado "varios" de una relación de varios a uno o de varios a varios entre los elementos de diferentes listas, y proporciona su carga diferida. Por ejemplo, suponga que cada miembro de un equipo se asigna a varios proyectos y que cada proyecto tiene varios miembros de equipo asignados. Esta es una relación de varios a varios. Una clase TeamMember representa el tipo de elementos de la lista Miembros del equipo y una clase Project representa el tipo de elementos de la lista Proyectos. La clase TeamMember declara un campo private de tipo EntitySet<TEntity> (donde TEntity es Project) que representa uno o varios elementos de la lista Proyectos. El campo está encapsulado en una propiedad public EntitySet<Proyecto> llamada AssignedProjects. Cuando se crea una instancia de un objeto del tipo TeamMember, el objeto al que hace referencia esta propiedad AssignedProjects no se carga inmediatamente. Se carga única y exclusivamente si se hace referencia a él en un código posterior.

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

Nota

En el ejemplo de código anterior, y otros que muestran la clase Project, hay una declaración de una propiedad Title que representa la columna Título. Ésta es una simplificación para facilitar su legibilidad. En el código real, la clase Project heredaría de una clase Item que representa el tipo de contenido base de SharePoint Foundation. Dado que la propiedad Title se declara en la definición de la clase Item, no estaría aquí en la definición de la clase Project.

Con estas declaraciones implementadas, puede consultar por miembros del equipo que estén asignados a un proyecto en particular, como en este ejemplo.

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;

Al igual que en el caso del ejemplo de EntityRef<TEntity>, pueden usarse cualquiera de las dos clases que representan los tipos de contenido de las dos listas relacionadas para definir la relación. A continuación, se muestra cómo se declararía la misma relación Miembros del equipo a Proyectos dentro de la clase 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.
}

Es posible declarar la relación en ambas clases. Para obtener más información, vea Búsquedas inversas a continuación.

Relaciones de uno a varios y de varios a uno

En la sección Referencias a entidades, hay un ejemplo de codificación de una relación uno a uno entre listas. En la sección Conjuntos de entidades, hay un ejemplo de codificación de una relación de varios a varios. Es posible representar relaciones de uno a varios y de varios a uno también. Para continuar con el ejemplo de la sección Conjuntos de entidades, suponga que cada miembro del equipo está asignado no solo a varios proyectos sino que además es coordinador de más de un proyecto, pero cada proyecto solo tiene un coordinador. La relación de liderazgo de la lista Miembros del equipo con la lista Proyectos es de uno a varios. Esta relación puede representarse con miembros EntitySet<TEntity> en la clase TeamMember y un miembro EntityRef<TEntity> en la clase Projects, como se muestra en el código siguiente.

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

Listas de búsqueda con varios valores permitidos

La clase LookupList<T> representa los valores actuales de un campo de búsqueda que permite varios valores. En consecuencia, es un subconjunto de valores que se encuentran en la columna de destino de la lista de objetivo en una relación de lista de búsqueda. Una clase de tipo de contenido puede tener un campo privado del tipo LookupList<T> para contener los valores buscados de la columna en el objeto de elemento de lista actual.

No es necesario tener este campo ya que se puede usar una propiedad EntitySet<TEntity> para representar una columna de búsqueda. Pero puede ser conveniente tenerlo debido a las diferencias en la forma en que se carga LookupList<T> y EntitySet<TEntity>. Considere dos clases de tipo de contenido, A y B, que son idénticas, salvo que A usa un campo LookupList<T> para representar una columna de búsqueda y B usa EntitySet<TEntity>. En ambos casos, cuando la consulta se enumera (normalmente en un bucle foreach), específicamente cuando el bucle alcanza un elemento de lista específico del tipo de contenido, realiza una consulta a la base de datos para rellenar las propiedades del objeto que representa el elemento de lista. Para un objeto de tipo A, el campo LookupList<T> se rellena inmediatamente con los valores de la columna de búsqueda. Las referencias posteriores a ese campo (o a una propiedad que la encapsule) en el bucle foreach no requieren una segunda consulta a la base de datos. En cambio, la carga se difiere para objetos del tipo EntitySet<TEntity>, por lo que cuando un bucle foreach llega a un objeto específico de tipo B, la propiedad EntitySet<TEntity>no se rellena inmediatamente con los elementos de la lista de objetivo sino que la carga se difiere hasta la primera vez en que se haga referencia a la propiedad en el cuerpo del bucle. Cuando se produce esa referencia, se requiere una segunda consulta a la base de datos para rellenar la propiedad.

Puede verse un ejemplo de una llamada a una propiedad EntitySet<TEntity> en el siguiente código de la sección Conjuntos de entidades. En dicho ejemplo, el campo AssignedProjects de la lista Miembros del equipo se configura como un campo de búsqueda del campo Título de la lista Proyectos, y se configura para permitir varios valores.

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;

Pero debido a que EntitySet<TEntity> usa carga diferida, el valor de la propiedad TeamMember.AssignedProjects debe consultarse desde la base de datos de contenido cada vez que se hace referencia a ella. Un objeto LookupList<T> se carga inmediatamente, por lo que el código que llama a una propiedad IList<T> que encapsula un campo LookupList<T> tiene un mejor rendimiento que el código que llama a la propiedad EntitySet<TEntity>.

Para continuar con el ejemplo, suponga que la clase TeamMember declara un campo private de tipo LookupList<T> (donde T es String) que representa los valores del campo AssignedProjects, que provienen de la columna de destino de búsqueda (Título) de la lista Proyectos. Por convención, este campo privado se designa con una concatenación (en la que se usan las mayúsculas y minúsculas como en Camel) del nombre de la columna de búsqueda y una versión pluralizada del nombre de la columna de destino de la que se extraen los valores. En este caso, el campo recibe el nombre de assignedProjectsTitles. El campo está encapsulado en una propiedad pública del tipo IList<String>. Por convención, se asigna como nombre una versión con mayúsculas iniciales del nombre del campo, por lo que en este caso la propiedad se denomina AssignedProjectsTitles.

Nota

Los nombres de la propiedad IList<T> y el campo LookupList<T> son plurales ya que el campo de búsqueda permite varios valores. La clase LookupList<T> no juega ningún papel en la representación de campos de búsqueda que permiten un solo valor únicamente.

Ahora, la clase TeamMember tiene declarada su relación con la lista Proyectos y tiene una propiedad AssignedProjectsTitles para hacer referencia a los valores del campo del elemento de lista Proyectos asignados. Lo que necesita es algo para combinar los dos. Esto se logra decorando la propiedad AssignedProjectsTitles con un atributo ColumnAttribute. Se establecen cuatro propiedades del atributo:

  • Name="Assigned Projects": especifica la columna cuyos valores se representan con la propiedad AssignedProjectsTitles.

  • FieldType="Lookup": especifica que el campo identificado con la propiedad Name es del tipo de búsqueda en SharePoint Foundation.

  • IsLookupValue=true – Especifica que el campo es un campo de búsqueda.

  • LookupDisplayColumn="Title": especifica la columna de destino de la lista de objetivo.

El siguiente código muestra las declaraciones necesarias para admitir la relación de búsqueda con una propiedad EntitySet<TEntity> y una propiedad IList<T> que encapsula un campo 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.
}

Con estas declaraciones implementadas, es posible obtener un mejor rendimiento de la consulta precedente, como se muestra en este ejemplo.

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

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

Nota

SPMetal genera una LookupList<T> solo cuando una columna de búsqueda apunta a una lista de objetivo que no esté representada por un código generado por SPMetal, como una lista de objetivo oculta o que se ha ejecutado a partir de la generación de código por el archivo de configuración de SPMetal. Vea Invalidación de valores predeterminados de SPMetal con un archivo XML de parámetros para obtener más información.

Los objetos ‘ID’ de LookupList especiales

El valor de un campo del tipo Lookup, una vez establecido, tiene la forma id;#display_value, donde display_value es el valor recuperado de la columna de destino de la lista de objetivo e id es el identificador del elemento de la lista de objetivo cuyo valor se seleccionó. Se suprime en la UI de SharePoint Foundation. Con frecuencia, cuando hay una relación de búsqueda entre listas y la columna de búsqueda permite varios valores, la solución necesitará tener acceso a los identificadores de los elementos de la lista de objetivo cuyos valores de la columna de destino son los valores de la columna de búsqueda. Puede hacerse referencia a estos valores a través de la propiedad EntitySet<TEntity> que representa los elementos de la lista de objetivo. Pero, en este caso también, el código se ejecuta de forma más rápida si dichos identificadores se almacenan en un campo LookupList<T>. Por esta razón, es aconsejable tener este campo y una propiedad IList<T> pública para encapsularlo, siempre que se tenga un campo LookupList<T> para contener los valores buscados.

La convención de nomenclatura para estas propiedades y campos ‘ID’ especiales es la misma que se aplica al campo y a la propiedad que contienen los valores buscados, salvo que la segunda parte de cada nombre es "Ids" en vez de una versión en plural del nombre de la columna de destino. El atributo [Column] de la propiedad IList<T> también es el mismo.

El ejemplo siguiente muestra las declaraciones de Miembros del equipo y Proyectos extendidas para agregar un campo ‘ID’ de LookupList<T> y su propiedad correspondiente.

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

Listas de búsqueda que solo permiten un valor

Un campo de búsqueda de un solo valor representa una relación uno a uno simple. Por lo tanto, el campo privado que almacena el valor es solo el tipo del campo de destino, en lugar del tipo LookupList<T>. De la misma forma, la propiedad pública que encapsula el campo es del mismo tipo, en lugar de IList. (En términos estrictos, el tipo del campo y la propiedad es el equivalente de Microsoft .NET Framework del tipo de SharePoint Foundation del campo de destino, según se explica en Asignación de tipo: Desde proveedor LINQ a SharePoint hasta .NET.) Asimismo, dado que solo hay un valor, los nombres del campo y de la propiedad no son plurales.

Considere una variación del ejemplo de la sección anterior. Suponga que cada miembro del equipo se asigna a un solo proyecto y que la asignación se registra en una columna Proyecto, que es un campo de búsqueda de la columna Título de la lista Proyectos. Además de usar un campo EntityRef<TEntity> para representar la relación entre las dos listas, la clase TeamMember también declararía un campo String privado de nombre projectTitle y una clase String pública para encapsularla de nombre ProjectTitle. Tenga en cuenta que si bien los nombres de los campos y propiedades son singulares, en lo demás siguen la convención de nombrar un valor de búsqueda mediante la concatenación del nombre del campo de búsqueda y el nombre del campo de destino.

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

Nota

En el caso especial de que el campo de búsqueda busque una variable en otro campo dentro de la misma tabla, no existe desde luego una relación entre las tablas. Por esta razón, las declaraciones del campo EntityRef<TEntity> y la propiedad que lo encapsula no estarían presentes.

Búsquedas inversas

Se puede declarar una relación entre listas en ambas clases de tipo de contenido. Esto permite realizar búsquedas inversas. Al hacerlo, no obstante, y si la relación es de varios a varios, la propiedad EntitySet<TEntity> de la lista de objetivo tiene un AssociationAttribute ligeramente distinto. En concreto, la propiedad MultivalueType se establece en AssociationType.Backward en vez de en Multi. Además, la propiedad Name se establece en el nombre interno de la columna de búsqueda de origen, es decir, el destino de una búsqueda inversa.

Vea también

Tareas

Procedimiento para usar SPMetal

Referencia

SPMetal

Conceptos

Seguimiento de cambios en objetos y concurrencia optimista